{"id":81955,"date":"2017-02-02T00:01:50","date_gmt":"2017-02-02T08:01:50","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/?p=81955"},"modified":"2019-02-18T09:10:12","modified_gmt":"2019-02-18T16:10:12","slug":"psscriptanalyzer-deep-dive-part-3-of-4","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/psscriptanalyzer-deep-dive-part-3-of-4\/","title":{"rendered":"PSScriptAnalyzer deep dive \u2013 Part 3 of 4"},"content":{"rendered":"<p><strong>Summary<\/strong>: Thomas Rayner, Microsoft Cloud and Datacenter Management MVP, shows how to use Pester to get nUnit formatted results out of PSScriptAnalyzer.<\/p>\n<p>Hello! I\u2019m Thomas Rayner, a Cloud and Datacenter Management Microsoft MVP, filling in for The Scripting Guy this week. You can find me on Twitter (<a target=\"_blank\" href=\"http:\/\/twitter.com\/MrThomasRayner\">@MrThomasRayner<\/a>), or posting on my blog, <a target=\"_blank\" href=\"http:\/\/workingsysadmin.com\">workingsysadmin.com<\/a>. This week, I\u2019m presenting a four-part series about how to use PSScriptAnalyzer.<\/p>\n<p><a target=\"_blank\" href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2017\/01\/31\/psscriptanalyzer-deep-dive-part-1-of-4\/\">Part 1 \u2013 Getting started with PSScriptAnalyzer<\/a><\/p>\n<p><a target=\"_blank\" href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2017\/02\/01\/psscriptanalyzer-deep-dive-part-2-of-4\/\">Part 2 \u2013 Suppressing, including, excluding rules<\/a><\/p>\n<p>Part 3 &#8211; Wrapping PSScriptAnalyzer with Pester to get formatted results<\/p>\n<p><a target=\"_blank\" href=\"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2017\/02\/03\/psscriptanalyzer-deep-dive-part-4-of-4\/\">Part 4 \u2013 Writing custom rules<\/a><\/p>\n<p>This is Part 3, and I\u2019m going to show you how to get nUnit formatted results from PSScriptAnalyzer.<\/p>\n<p>nUnit is an XML based open source test result format. nUnit is a testing format that\u2019s used in popular Continuous Integration and Continuous Deployment services like AppVeyor and Visual Studio Team Services. There are ways to get differently formatted and recorded output from PSScriptAnalyzer. By default, most people I\u2019ve worked with have found it easiest to use Pester to format their test results.<\/p>\n<p>Pester is a domain-specific language that was developed to test PowerShell code. There are a ton of great resources to learn Pester. If what you see in this post is very unfamiliar, I\u2019d recommend finding a book, online training course, blog, or other resource to get started. The <a target=\"_blank\" href=\"https:\/\/github.com\/pester\/pester\">Pester GitHub page<\/a> is a great place to start.<\/p>\n<p style=\"padding-left: 30px\"><strong>Note<\/strong>: Procedures in this post are for Pester version 3.4.6 and haven&#8217;t been tested on 4.x yet.<\/p>\n<p>If you don\u2019t already have Pester installed, you can get it from the <a target=\"_blank\" href=\"https:\/\/www.powershellgallery.com\/\">PowerShell Gallery<\/a> and import the module.<\/p>\n<p style=\"padding-left: 60px\"><code>Install-Module -Name Pester -Scope CurrentUser<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>Import-Module -Name Pester<\/code><\/p>\n<p>Now you\u2019re ready to do some Pester testing.<\/p>\n<p>First, let\u2019s define the script, named MyScript.ps1, that we want to evaluate.<\/p>\n<p style=\"padding-left: 60px\"><code>param (<\/code><\/p>\n<p style=\"padding-left: 90px\"><code>$Path,\n$DaysOld<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>)\n$someVar = $null\nWrite-Host \"Counting items...\"\n$itemCount = (gci $Path | ? { $_.LastWriteTime -gt (Get-Date).AddDays(-$DaysOld)}).Count\nWrite-Host \"There are $itemCount items\"<\/code><\/p>\n<p>There are some clear problems with this already, but, if it were perfect, it wouldn\u2019t generate interesting PSSA output.<\/p>\n<p>Now, I\u2019m going to write a Pester test. I\u2019ll start by declaring a <code>Describe<\/code> and <code>Context<\/code> block.<\/p>\n<p style=\"padding-left: 60px\"><code>Describe 'Testing against PSSA rules' {<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>Context 'PSSA Standard Rules' {<\/code><\/p>\n<p style=\"padding-left: 90px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>}<\/code><\/p>\n<p>Now, something should probably go inside, right? I\u2019m going to run PSSA against my script, store the variables in <code>$analysis<\/code>, and get a list of rules stored in <code>$scriptAnalyzerRules<\/code>. (I could also specify a location to custom rules here if I had any.)<\/p>\n<p style=\"padding-left: 60px\"><code>$analysis = Invoke-ScriptAnalyzer -Path '.\\MyScript.ps1'<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>$scriptAnalyzerRules = Get-ScriptAnalyzerRule<\/code><\/p>\n<p>Why did I do this? What I\u2019m going to do next is use a <code>foreach<\/code> loop to build all my <code>It<\/code> statements, and test for all the rules we\u2019re evaluating.<\/p>\n<p style=\"padding-left: 60px\"><code>forEach ($rule in $scriptAnalyzerRules) {<\/code><\/p>\n<p style=\"padding-left: 90px\"><code>It \"Should pass $rule\" {<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>If ($analysis.RuleName -contains $rule) {<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>$analysis |<\/code><\/p>\n<p style=\"padding-left: 240px\"><code>Where RuleName -EQ $rule -outvariable failures |\nOut-Default<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>$failures.Count | Should Be 0<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 90px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>}<\/code><\/p>\n<p>Here, I\u2019m looping through all the rules that PSSA checked and declaring that my script should pass that rule. Any PSSA rule violations are stored in <code>$analysis<\/code>. So, if <code>$analysis.RuleName<\/code> has an entry in it that matches a rule, I know that this assertion failed.<\/p>\n<p>Here\u2019s what my full test file, MyTest.tests.ps1, looks like.<\/p>\n<p style=\"padding-left: 60px\"><code>Describe 'Testing against PSSA rules' {<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>Context 'PSSA Standard Rules' {<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>$analysis = Invoke-ScriptAnalyzer -Path\u00a0 '.\\MyScript.ps1'<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>$scriptAnalyzerRules = Get-ScriptAnalyzerRule<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>forEach ($rule in $scriptAnalyzerRules) {<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>It \"Should pass $rule\" {<\/code><\/p>\n<p style=\"padding-left: 180px\"><code>If ($analysis.RuleName -contains $rule) {<\/code><\/p>\n<p style=\"padding-left: 210px\"><code>$analysis |<\/code><\/p>\n<p style=\"padding-left: 300px\"><code>Where RuleName -EQ $rule -outvariable failures |\nOut-Default<\/code><\/p>\n<p style=\"padding-left: 210px\"><code>$failures.Count | Should Be 0<\/code><\/p>\n<p style=\"padding-left: 180px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 150px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 120px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 90px\"><code>}<\/code><\/p>\n<p style=\"padding-left: 60px\"><code>}<\/code><\/p>\n<p>Now, I just need to run one line to get my nUnit formatted test results.<\/p>\n<p style=\"padding-left: 60px\"><code>Invoke-Pester -OutputFile 'PSSAResults.xml' -OutputFormat 'LegacyNUnitXml' -Script '.\\MyTest.tests.ps1'<\/code><\/p>\n<p>Done! Presumably, if you\u2019re doing this, it\u2019s to interface with some Continuous Integration\/Deployment service like AppVeyor or Visual Studio Team Services. You will have a couple steps left to integrate this solution seamlessly into your Continuous Integration\/Deployment suite of choice to properly collect and act on the test results. This is the funky part \u2013 getting nUnit formatted results from PSSA.<\/p>\n<p>There are other ways to get the same result, but this is a pretty easy, scalable solution that I happen to like. If it doesn\u2019t work for you for some reason, I\u2019d love to hear about your alternative!<\/p>\n<p>Now, if you\u2019ve integrated this into Visual Studio Team Services, for example, you\u2019ll get some cool screens like these:<\/p>\n<p>Looking at the test results in Visual Studio Team Services.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1-HSG-020217.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/1-HSG-020217.png\" alt=\"Build results that include Pester results\" width=\"604\" height=\"406\" class=\"alignnone size-full wp-image-81965\" \/><\/a><\/p>\n<p>Reviewing detailed results for the PSSA results to see which rules were violated.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2-HSG-020217.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/2-HSG-020217.png\" alt=\"Pass \/ fail results\" width=\"411\" height=\"151\" class=\"alignnone size-full wp-image-81975\" \/><\/a><\/p>\n<p>I have a much longer post on my blog that explains how to set this up Visual Studio Team Services. If that interests you, feel free to check out <a target=\"_blank\" href=\"http:\/\/www.workingsysadmin.com\/invoking-pester-and-psscriptanalyzer-tests-in-hosted-vsts\">Invoking Pester and PSScriptAnalyzer Tests in Hosted VSTS<\/a>.<\/p>\n<p>Come back tomorrow, and I\u2019m going to show you how to write your own customized PSScriptAnalyzer rules.<\/p>\n<p>Thomas, thanks for that update!\u00a0 Using Pester as an addition tool is certainly an excellent way to QA my tools before going into production!<\/p>\n<p>I invite you to follow the Scripting Guys on <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguystwitter\">Twitter<\/a> and <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingguysfacebook\">Facebook<\/a>. If you have any questions, send email to them at <a target=\"_blank\" href=\"mailto:scripter@microsoft.com\">scripter@microsoft.com<\/a>, or post your questions on the <a target=\"_blank\" href=\"http:\/\/bit.ly\/scriptingforum\">Official Scripting Guys Forum<\/a>. See you tomorrow.<\/p>\n<p>Until then, always remember that with Great PowerShell comes Great Responsibility.<\/p>\n<p><strong>Sean Kearney\n<\/strong>Honorary Scripting Guy\nCloud and Datacenter Management MVP<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Thomas Rayner, Microsoft Cloud and Datacenter Management MVP, shows how to use Pester to get nUnit formatted results out of PSScriptAnalyzer. Hello! I\u2019m Thomas Rayner, a Cloud and Datacenter Management Microsoft MVP, filling in for The Scripting Guy this week. You can find me on Twitter (@MrThomasRayner), or posting on my blog, workingsysadmin.com. This [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[568,685,641],"tags":[56,652,45],"class_list":["post-81955","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hey-scripting-guy","category-scripting-techniques","category-windows-powershell","tag-guest-blogger","tag-thomas-rayner","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Thomas Rayner, Microsoft Cloud and Datacenter Management MVP, shows how to use Pester to get nUnit formatted results out of PSScriptAnalyzer. Hello! I\u2019m Thomas Rayner, a Cloud and Datacenter Management Microsoft MVP, filling in for The Scripting Guy this week. You can find me on Twitter (@MrThomasRayner), or posting on my blog, workingsysadmin.com. This [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/81955","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=81955"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/81955\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=81955"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=81955"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=81955"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}