{"id":12295,"date":"2016-06-24T06:00:00","date_gmt":"2016-06-24T13:00:00","guid":{"rendered":"https:\/\/www.sapien.com\/blog\/?p=12295"},"modified":"2016-07-13T11:38:50","modified_gmt":"2016-07-13T18:38:50","slug":"testing-pester-code-coverage","status":"publish","type":"post","link":"https:\/\/dev.sapien.com\/blog\/2016\/06\/24\/testing-pester-code-coverage\/","title":{"rendered":"Testing Pester Code Coverage"},"content":{"rendered":"<p>Applies to: PowerShell 5.0.10586.122, 5.1.14367, Pester 3.4.0<\/p>\n<p>I&#8217;m one of those people who thinks a score of 99% is failing, so I love to see those 100% scores when I use the <strong>CodeCoverage<\/strong> parameter of <strong>Invoke-Pester<\/strong>.<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image002-3.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image002\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image002_thumb-3.jpg\" alt=\"clip_image002\" width=\"657\" height=\"156\" border=\"0\" \/><\/a><\/p>\n<p>But, while assembling my Pester presentations for <a href=\"https:\/\/www.youtube.com\/watch?v=jvvh9cpD_LM\" target=\"_new\">DevOps Global Summit 2016<\/a> and <a href=\"https:\/\/www.youtube.com\/watch?v=gssAtCeMOoo\" target=\"_new\">PowerShell Conference Europe 2016<\/a>, I realized the 100% code coverage score means that 100% of my code (every line) ran during the test. It doesn&#8217;t mean that 100% of my code is tested. Code coverage reports are really valuable, but you need to understand what they test and how to use them.<\/p>\n<p>This post is the fourth in a series about how to run Pester tests. <\/p>\n<blockquote><p>\nSee the posts in this Pester series:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.sapien.com\/blog\/2016\/06\/13\/how-to-run-a-pester-test\/\" target=\"_new\">How to Run a Pester Test<\/a><\/li>\n<li><a href=\"https:\/\/www.sapien.com\/blog\/2016\/06\/15\/invoke-pester-run-selected-tests\/\" target=\"_new\">Invoke-Pester: Running Selected Tests<\/a><\/li>\n<li><a href=\"https:\/\/www.sapien.com\/blog\/2016\/06\/17\/how-to-pass-parameters-to-a-pester-test-script\/\" target=\"_new\">How to Pass Parameters to a Pester Test Script<\/a><\/li>\n<li><a href=\"https:\/\/www.sapien.com\/blog\/2016\/06\/24\/testing-pester-code-coverage\/\" target=\"_new\">Testing Pester Code Coverage<\/a><\/li>\n<\/ul>\n<\/blockquote>\n<h1>What is code coverage?<\/h1>\n<p>Among the many amazing features of the Pester test framework for PowerShell is a code coverage test. To run it, you use the <b>CodeCoverage<\/b> parameter of Invoke-Pester.<\/p>\n<p>So, what&#8217;s code coverage?<\/p>\n<blockquote><p><i>&#8220;&#8230; describe(s) the degree to which the source code of a program is tested by a particular test suite. A program with high code coverage has been more thoroughly tested and has a lower chance of containing software bugs than a program with low code coverage&#8230;&#8221; <\/i><br \/>\n<a href=\"https:\/\/en.wikipedia.org\/wiki\/Code_coverage\">Wikipedia: &#8220;code coverage&#8221;<\/a><\/p><\/blockquote>\n<p>Ah, it&#8217;s a measure of the code testing.<\/p>\n<p>But, at least in this case, I found out that it&#8217;s a measure of the <b><em>code that runs<\/em><\/b> during a test; not the degree to which the code is tested or anything about the quality of the test.<\/p>\n<h1>Invoke-Pester -CodeCoverage<\/h1>\n<p>The <b>CodeCoverage<\/b> parameter of Invoke-Pester adds a code coverage report to the tests that Pester runs. A code coverage report lists the lines of code that did and did not run during a Pester test.<\/p>\n<pre class=\"output\">Code coverage report:\r\nCovered 77.78 % of 9 analyzed commands in 1 file.\r\n\r\nMissed commands:\r\n\r\nFile      Function      Line Command\r\n----      --------      ---- -------\r\nHello.ps1 Test-LeapYear 30   Write-Warning \"$($date.Year) is not a leap year.\"\r\nHello.ps1 Test-LeapYear 30   $date.Year<\/pre>\n<p>It seems like -CodeCoverage should be a switch parameter, but it requires strings and\/or hash table values. In its simplest form, it takes a path string. But, be careful! <b>Enter the path to the code being tested<\/b>; not the path to the test file.<\/p>\n<p>For example, if you have Get-Script.ps1 and its test file, Get-Script.Tests.ps1, the value of the <b>Script<\/b> parameter is Get-Script.Tests.ps1, but the value of the <b>CodeCoverage<\/b> parameter is Get-Script.ps1.<\/p>\n<p>This makes sense. You want the Code Coverage report to make sure the code <b><i>in your script<\/i><\/b> ran. The primary output already tells you which tests ran.<\/p>\n<p>By default, Invoke-Pester writes the code coverage report to the host program (like Write-Host), so you can&#8217;t save it in a variable, redirect it, or pipe it to a file.<\/p>\n<p>To save your Code Coverage report, use the <b>PassThru<\/b> parameter. The custom object that Invoke-Pester returns has an additional CodeCoverage property that contains a custom object with detailed results of the code coverage test, including lines hit, lines missed, and helpful statistics.<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image004-1.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image004\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image004_thumb-1.jpg\" alt=\"clip_image004\" width=\"897\" height=\"287\" border=\"0\" \/><\/a><\/p>\n<p>You can pipe the code coverage custom object to Export-Clixml or to ConvertTo-Json and then to Out-File. However, NUnitXML and LegacyNUnitXML output that the OutputXML and OutputFormat parameters of Invoke-Pester use do not include any code coverage information, because it&#8217;s not supported by the schema.<\/p>\n<p>Oh, and if you use the Quiet parameter to suppress the host output of Invoke-Pester, it suppresses the Code Coverage report, too.<\/p>\n<h1>What does the Code Coverage parameter do?<\/h1>\n<p>To identify what the CodeCoverage parameter tests, let&#8217;s experiment.<\/p>\n<p>Here&#8217;s a little test script, Hello.ps1. It consists of three functions:<\/p>\n<ul>\n<li><b>Get-Hello<\/b> function writes &#8216;Hello, World&#8221; and calls the <b>Test-LeapYear<\/b> function.<\/li>\n<li><b>Test-LeapYear<\/b> calls a <b>Get-DateHelper<\/b> function, then calls the <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/system.datetime.isleapyear(v=vs.110).aspx\">IsLeapYear<\/a> static method.<\/li>\n<li><b>Get-DateHelper<\/b> calls the Get-Date cmdlet. I added Get-DateHelper so I could mock it and provide varying test input to Test-LeapYear.<\/li>\n<\/ul>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image005-1.png\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image005\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image005_thumb-1.png\" alt=\"clip_image005\" width=\"710\" height=\"519\" border=\"0\" \/><\/a><\/p>\n<p>Here&#8217;s the corresponding Hello.Tests.ps1 script. Note that it tests only Get-Hello and only verifies that it returns &#8220;Hello, World.&#8221;<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image007.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image007\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image007_thumb.jpg\" alt=\"clip_image007\" width=\"732\" height=\"195\" border=\"0\" \/><\/a><\/p>\n<p>So, you would expect the Code Coverage report to be pretty dismal, but it&#8217;s not too bad. It just reports that the Else clause of Test-LeapYear didn&#8217;t run. But, it doesn&#8217;t report the functionality of Test-LeapYear isn&#8217;t tested at all and Get-DateHelper aren&#8217;t tested at all.<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image009.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image009\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image009_thumb.jpg\" alt=\"clip_image009\" width=\"744\" height=\"241\" border=\"0\" \/><\/a><\/p>\n<p>In fact, if you comment-out the Else clause of Test-LeapYear, the code coverage result is surprising good.<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image011.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image011\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image011_thumb.jpg\" alt=\"clip_image011\" width=\"755\" height=\"552\" border=\"0\" \/><\/a><\/p>\n<p>Much too good, given that most of the script isn&#8217;t tested.<\/p>\n<p><a href=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image013.jpg\"><img loading=\"lazy\" decoding=\"async\" style=\"background-image: none; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-width: 0px;\" title=\"clip_image013\" src=\"https:\/\/www.sapien.com\/blog\/wp-content\/uploads\/2016\/06\/clip_image013_thumb.jpg\" alt=\"clip_image013\" width=\"762\" height=\"181\" border=\"0\" \/><\/a><\/p>\n<p>The Code Coverage report lists the lines of code in the script that ran (and didn&#8217;t run) during the test. In the first version, the Else clause never ran because the current year, 2016, is a leap year.<\/p>\n<p><b>It does not evaluate the tests. It doesn&#8217;t verify that the lines of code are tested; only that they ran during the test. <\/b><\/p>\n<p>Notice that this example uses Write-Warning, which writes to the <a href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/03\/30\/understanding-streams-redirection-and-write-host-in-powershell\/\">warning stream<\/a>, not Write-Output. If I use Write-Output, or any other cmdlet that writes to the output stream (stdout), the current simplistic test for Get-Hello would fail, but CodeCoverage would still be 100%.<\/p>\n<h1>Customizing Code Coverage<\/h1>\n<p>By default, the CodeCoverage parameter evaluates entire script files, but you can limit it to specific functions or even lines in the files. To customize your CodeCoverage value, use a hash table.<\/p>\n<p>For example, this command evaluates code coverage in the Hello.ps1 file while running tests in the Hello.Tests.ps1 file. (You can also include a string array value with wildcard characters, but this is a simple case.)<\/p>\n<pre lang=\"PowerShell\">Invoke-Pester -Script Hello.Tests.ps1 -CodeCoverage Hello.ps1<\/pre>\n<p>This command evaluates only for the Test-LeapYear function.<\/p>\n<pre lang=\"PowerShell\">Invoke-Pester -Script Hello.Tests.ps1 -CodeCoverage @{Path = 'Hello.ps1'; Function = 'Test-LeapYear'}<\/pre>\n<p>This command evaluates only for the functions in Hello.ps1 with the Get verb.<\/p>\n<pre lang=\"PowerShell\">Invoke-Pester -Script Hello.Tests.ps1 -CodeCoverage @{Path = 'Hello.ps1'; Function = 'Test-LeapYear'}<\/pre>\n<p>You can be very specific. This command evaluates only lines 23 &#8211; 26 in Hello.ps1. The defaults for StartLine and EndLine are the first and last lines of the script, respectively.<\/p>\n<pre lang=\"PowerShell\">Invoke-Pester -Script Hello.Tests.ps1 -CodeCoverage @{Path = 'Hello.ps1'; StartLine = 23; EndLine = 26 }<\/pre>\n<p>And, you can use aliases for the Path (P), Function (F), StartLine (S), and EndLine (E) keys, although I wouldn&#8217;t do that. As a best practice, I omit aliases and abbreviations in shared code.<\/p>\n<pre lang=\"PowerShell\">Invoke-Pester -Script Hello.Tests.ps1 -CodeCoverage @{P = 'Hello.ps1'; F = 'Test-LeapYear'}<\/pre>\n<h1>Use Code Coverage<\/h1>\n<p>So, in summary, use the CodeCoverage parameter &#8212; I do! &#8212; but don\u2019t get too excited about a 100% result, or any surprisingly positive or negative result. Always test your tests and, in the immortal words of whomever Dave Wyatt quotes, &#8220;Never trust a test that won&#8217;t fail.&#8221;<\/p>\n<p>&nbsp;<\/p>\n<p>Learning Pester? Check out <a title=\"Real-World Test-Driven Development with Pester\" href=\"https:\/\/www.youtube.com\/watch?v=gssAtCeMOoo\" target=\"_blank\">Real-World Test-Driven Development with Pester<\/a>. The code and slides are in Github at <a href=\"https:\/\/github.com\/juneb\/PesterTDD\">https:\/\/github.com\/juneb\/PesterTDD<\/a>.<\/p>\n<p>This post is the fourth in a series about how to run Pester tests. See also: <a href=\"https:\/\/wp.me\/p3tXTf-3aE\" target=\"_new\">How to Run Pester Tests<\/a>, <a href=\"https:\/\/wp.me\/p3tXTf-3aN\" target=\"_new\">Invoke-Pester: Run Selected Tests<\/a>, and <a href=\"https:\/\/www.sapien.com\/blog\/2016\/06\/17\/how-to-pass-parameters-to-a-pester-test-script\/\" target=\"_new\">How to Pass Parameters to a Pester Test Script<\/a>.<\/p>\n<p><em>June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at <\/em><a href=\"mailto:juneb@sapien.com\"><em>juneb@sapien.com <\/em><\/a><em>or follow her on Twitter at <\/em><a href=\"https:\/\/www.twitter.com\/juneb_get_help\"><em>@juneb_get_help<\/em><\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Applies to: PowerShell 5.0.10586.122, 5.1.14367, Pester 3.4.0 I&#8217;m one of those people who thinks a score of 99% is failing, so I love to see those 100% scores when I use the CodeCoverage parameter of Invoke-Pester. But, while assembling my Pester presentations for DevOps Global Summit 2016 and PowerShell Conference Europe 2016, I realized the [&hellip;]<\/p>\n","protected":false},"author":31,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[1119,1121,1120],"tags":[1136,1137,1127,1123,28,1135,622,997],"class_list":["post-12295","post","type-post","status-publish","format-standard","hentry","category-pester","category-test-driven-development","category-testing","tag-behavior-driven-development-bdd","tag-code-coverage","tag-invoke-pester","tag-pester","tag-powershell","tag-test-driven-development-tdd","tag-testing","tag-windows-powershell"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/12295","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/comments?post=12295"}],"version-history":[{"count":18,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/12295\/revisions"}],"predecessor-version":[{"id":12502,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/posts\/12295\/revisions\/12502"}],"wp:attachment":[{"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/media?parent=12295"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/categories?post=12295"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dev.sapien.com\/blog\/wp-json\/wp\/v2\/tags?post=12295"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}