1"use strict";(self.webpackChunkpester_docs=self.webpackChunkpester_docs||[]).push([["5089"],{5360(e,n,s){s.d(n,{Ay:()=>l,RM:()=>r});var t=s(1987),i=s(7008);let r=[];function o(e){let n={code:"code",p:"p",pre:"pre",...(0,i.R)(),...e.components},{Details:s}=n;return s||function(e,n){throw Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,t.jsxs)(s,{children:[(0,t.jsx)("summary",{children:"Required code for samples (PowerShell 6 or later)"}),(0,t.jsxs)("div",{children:[(0,t.jsx)(n.p,{children:"To run the sample below, place this file in the same folder as Get-Emoji.Tests.ps1."}),(0,t.jsx)(n.pre,{children:(0,t.jsx)(n.code,{className:"language-powershell",metastring:'title="Get-Emoji.ps1"',children:"$emojis = @(\n @{ Name = 'apple'; Symbol = '\u{1F34E}'; Kind = 'Fruit' }\n @{ Name = 'beaming face with smiling eyes'; Symbol = '\u{1F601}'; Kind = 'Face' }\n @{ Name = 'cactus'; Symbol = '\u{1F335}'; Kind = 'Plant' }\n @{ Name = 'giraffe'; Symbol = '\u{1F992}'; Kind = 'Animal' }\n @{ Name = 'pencil'; Symbol = '\u270F\uFE0F'; Kind = 'Item' }\n @{ Name = 'penguin'; Symbol = '\u{1F427}'; Kind = 'Animal' }\n @{ Name = 'pensive'; Symbol = '\u{1F614}'; Kind = 'Face' }\n @{ Name = 'slightly smiling face'; Symbol = '\u{1F642}'; Kind = 'Face' }\n @{ Name = 'smiling face with smiling eyes'; Symbol = '\u{1F60A}'; Kind = 'Face' }\n) | ForEach-Object { [PSCustomObject]$_ }\n\nfunction Get-Emoji ([string]$Name = '*') {\n $emojis | Where-Object Name -like $Name | ForEach-Object Symbol\n}\n"})})]})]})}function l(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,t.jsx)(n,{...e,children:(0,t.jsx)(o,{...e})}):o(e)}},2204(e,n,s){s.r(n),s.d(n,{metadata:()=>t,default:()=>u,frontMatter:()=>l,contentTitle:()=>c,toc:()=>a,assets:()=>d});var t=JSON.parse('{"id":"usage/discovery-and-run","title":"Discovery and Run","description":"Pester runs your test files in two phases, Discovery and Run. This introduction explains how they work and how it affects they way your structure and write your tests","source":"@site/versioned_docs/version-v5/usage/discovery-and-run.mdx","sourceDirName":"usage","slug":"/usage/discovery-and-run","permalink":"/docs/v5/usage/discovery-and-run","draft":false,"unlisted":false,"editUrl":"https://github.com/pester/docs/edit/main/versioned_docs/version-v5/usage/discovery-and-run.mdx","tags":[],"version":"v5","frontMatter":{"title":"Discovery and Run","description":"Pester runs your test files in two phases, Discovery and Run. This introduction explains how they work and how it affects they way your structure and write your tests"},"sidebar":"docs","previous":{"title":"Test file structure","permalink":"/docs/v5/usage/test-file-structure"},"next":{"title":"Data driven tests","permalink":"/docs/v5/usage/data-driven-tests"}}'),i=s(1987),r=s(7008),o=s(5360);let l={title:"Discovery and Run",description:"Pester runs your test files in two phases, Discovery and Run. This introduction explains how they work and how it affects they way your structure and write your tests"},c,d={},a=[{value:"Execution order",id:"execution-order",level:2},{value:"Discovery",id:"discovery",level:3},...o.RM,{value:"Run",id:"run",level:3},{value:"Common gotchas",id:"common-gotchas",level:2},{value:"<code>BeforeAll</code> and <code>-TestCases</code>",id:"beforeall-and--testcases",level:3},{value:"Generating test and blocks via <code>foreach</code> keyword",id:"generating-test-and-blocks-via-foreach-keyword",level:3},{value:"Variables are not available in test",id:"variables-are-not-available-in-test",level:3}];function h(e){let n={a:"a",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.p,{children:"Pester runs your test files in two phases: Discovery and Run. During discovery, it quickly scans your test files and discovers all the Describes, Contexts, Its and other Pester blocks."}),"\n",(0,i.jsx)(n.p,{children:"This powers many of the features in Pester 5 and enables many others to be implemented in the future."}),"\n",(0,i.jsx)(n.p,{children:"To reap the benefits, there are two rules to follow:"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Put all your test-code into ",(0,i.jsx)(n.code,{children:"It"}),", ",(0,i.jsx)(n.code,{children:"BeforeAll"}),", ",(0,i.jsx)(n.code,{children:"BeforeEach"}),", ",(0,i.jsx)(n.code,{children:"AfterAll"})," or ",(0,i.jsx)(n.code,{children:"AfterEach"}),"."]})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Put no test-code directly into ",(0,i.jsx)(n.code,{children:"Describe"}),", ",(0,i.jsx)(n.code,{children:"Context"})," or on the top of your file, without wrapping it in one of these blocks."]})}),"\n",(0,i.jsx)(n.p,{children:"All misplaced code will run during Discovery, and its results won't be available during Run. This will lead to confusing results."}),"\n",(0,i.jsxs)(n.p,{children:["Running code in Discovery is used for dynamically creating tests. That code should be placed into ",(0,i.jsx)(n.code,{children:"BeforeDiscovery"}),", see ",(0,i.jsx)(n.a,{href:"data-driven-tests#beforediscovery",children:"Data driven tests"}),"."]}),"\n",(0,i.jsx)(n.h2,{id:"execution-order",children:"Execution order"}),"\n",(0,i.jsxs)(n.p,{children:["In Pester v4 the execution of your test code was mostly top-down, with the exception of all the ",(0,i.jsx)(n.code,{children:"Before*"})," and ",(0,i.jsx)(n.code,{children:"After*"}),' blocks. Those blocks were picked up by Pester using AST and "moved" to the place where they needed to run.']}),"\n",(0,i.jsxs)(n.p,{children:["For example placing an ",(0,i.jsx)(n.code,{children:"AfterAll"})," at the top of a ",(0,i.jsx)(n.code,{children:"Describe"})," would not run it at the top, but instead it would correctly run it as the last thing in that ",(0,i.jsx)(n.code,{children:"Describe"})," wrapped in a ",(0,i.jsx)(n.code,{children:"try finally"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Pester v5 takes this to the next level and manipulates the execution of your test file in a more complex way. This has good reason, but makes understanding how your code executes quite complex, especially when you don't follow the two rules listed above."}),"\n",(0,i.jsx)(n.h3,{id:"discovery",children:"Discovery"}),"\n",(0,i.jsx)(n.p,{children:"Take for example this test file. When executed it will discover two tests inside of this file, and run them:"}),"\n","\n",(0,i.jsx)(o.Ay,{}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-powershell",metastring:'title="Get-Emoji.Tests.ps1"',children:'BeforeAll {\n . $PSCommandPath.Replace(\'.Tests.ps1\',\'.ps1\')\n}\n\nDescribe "Get-Emoji" {\n It "Returns <expected> (<name>)" -TestCases @(\n @{ Name = "cactus"; Expected = \'\u{1F335}\'}\n @{ Name = "giraffe"; Expected = \'\u{1F992}\'}
1\n ) {\n Get-Emoji -Name $name | Should -Be $expected\n }\n}\n\nWrite-Host "Discovery done."\n'})}),"\n",(0,i.jsxs)(n.p,{children:["This is what happens step by step when you run ",(0,i.jsx)(n.code,{children:"Get-Emoji.Tests.ps1"}),":"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The script file ",(0,i.jsx)(n.code,{children:"Get-Emoji.Tests.ps1"})," is invoked"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"BeforeAll"})," function runs. It saves the ScriptBlock provided to it, but does not execute it yet."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"Describe"})," function runs. It ",(0,i.jsx)(n.strong,{children:"invokes"})," the ScriptBlock provided to it, to be able to collect information about the tests contained inside of that scriptblock. ",(0,i.jsx)(n.code,{children:"Describe"})," and ",(0,i.jsx)(n.code,{children:"Context"})," scriptblocks are the only scriptblocks that are invoked during ",(0,i.jsx)(n.code,{children:"Discovery"}),"."]}),"\n",(0,i.jsxs)(n.li,{children:["All parameters provided to ",(0,i.jsx)(n.code,{children:"It"})," are evaluated by PowerShell:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"-Name"})," is evaluated:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["if double quoted string is used (",(0,i.jsx)(n.code,{children:'""'}),") all variables and sub-expressions in the string are expanded"]}),"\n",(0,i.jsxs)(n.li,{children:["if single quotes string is used (",(0,i.jsx)(n.code,{children:"''"}),") it is taken as it is"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"-TestCases"})," are evaluated:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["all ",(0,i.jsx)(n.code,{children:'""'})," strings are expanded"]}),"\n",(0,i.jsx)(n.li,{children:"all commands are executed, if any are used"}),"\n"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"It"})," function runs. The ScriptBlock provided (the body of the test) is saved but not executed yet. Two tests are generated. One for each item in ",(0,i.jsx)(n.code,{children:"-TestCases"})," array."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"Write-Host"}),' runs and prints "Discovery done."']}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["At this point we are done with ",(0,i.jsx)(n.code,{children:"Discovery"}),'. As you can see discovery is just a "trick". We run the whole script to the end, but don\'t invoke any ScriptBlock, except for the one associated with ',(0,i.jsx)(n.code,{children:"Describe"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["At the end of ",(0,i.jsx)(n.code,{children:"Discovery"})," we have collected all the tests and setups that are contained in the file, and stored them in the internal data of Pester module. The internal representation looks something like this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"Container: Get-Emoji.Tests.ps1\n BeforeAll: `{ . $PSCommandPath.Replace('.Tests.ps1','.ps1') }\n AfterAll: <none>\n Blocks:\n Block: Get-Emoji\n BeforeAll: <none>\n BeforeEach: <none>\n AfterEach: <none>\n AfterAll: <none>\n Tests:\n Test: Returns <expected> (<name>)\n Data: @{ Name = \"cactus\"; Expected = '\u{1F335}'}
1\n ScriptBlock: { Get-Emoji -Name $name | Should -Be $expected }\n\n Test: Returns <expected> (<name>)\n Data: @{ Name = \"giraffe\"; Expected = '\u{1F992}'}\n ScriptBlock: { Get-Emoji -Name $name | Should -Be $expected }\n"})}),"\n",(0,i.jsx)(n.p,{children:"Now we would go through all the objects, check filters and propagate them down and back up. This gives us the final overview of what needs to run. In this case there are no filters, so nothing changes and all tests will be invoked."}),"\n",(0,i.jsxs)(n.p,{children:["At this point we are done with the Discovery phase. We invoked the script to the end, and evaluated all the ",(0,i.jsx)(n.code,{children:"-TestCases"})," and ",(0,i.jsx)(n.code,{children:"-Name"})," strings if they have variables in them and use double quotes."]}),"\n",(0,i.jsx)(n.h3,{id:"run",children:"Run"}),"\n",(0,i.jsx)(n.p,{children:"In the run phase we take the internal tree of containers, blocks and tests and run them. We need to do quite a lot of work to reconstruct the correct scoping and to ensure that everything runs safely. At a high level this is what happens:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Run the ",(0,i.jsx)(n.code,{children:"BeforeAll"})," ScriptBlock, this imports the tested function"]}),"\n",(0,i.jsxs)(n.li,{children:["Create new scope to isolate the test from other tests","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Invoke the ",(0,i.jsx)(n.code,{children:"It ScriptBlock"}),", this runs the first test"]}),"\n",(0,i.jsx)(n.li,{children:"Return to the previous scope"}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["Create new scope to isolate the test from other tests","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Invoke the ",(0,i.jsx)(n.code,{children:"It ScriptBlock"}),", this runs the second test"]}),"\n",(0,i.jsx)(n.li,{children:"Return to the previous scope"}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.li,{children:"Return to the previous scope"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"We are done. All tests in this container are executed."}),"\n",(0,i.jsx)(n.h2,{id:"common-gotchas",children:"Common gotchas"}),"\n",(0,i.jsxs)(n.h3,{id:"beforeall-and--testcases",children:[(0,i.jsx)(n.code,{children:"BeforeAll"})," and ",(0,i.jsx)(n.code,{children:"-TestCases"})]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"-TestCases"})," are evaluated during ",(0,i.jsx)(n.code,{children:"Discovery"}),", but ",(0,i.jsx)(n.code,{children:"BeforeAll"})," won't run until the ",(0,i.jsx)(n.code,{children:"Run"})," phase. Using variables set in ",(0,i.jsx)(n.code,{children:"BeforeAll"})," in ",(0,i.jsx)(n.code,{children:"-TestCases"})," (or ",(0,i.jsx)(n.code,{children:"-ForEach"}),") won't work. The variable from ",(0,i.jsx)(n.code,{children:"BeforeAll"})," simply won't be defined until much after ",(0,i.jsx)(n.code,{children:"-TestCases"})," and ",(0,i.jsx)(n.code,{children:"-ForEach"})," are evaluated."]}),"\n",(0,i.jsxs)(n.h3,{id:"generating-test-and-blocks-via-foreach-keyword",children:["Generating test and blocks via ",(0,i.jsx)(n.code,{children:"foreach"})," keyword"]}),"\n",(0,i.jsxs)(n.p,{children:["A common pattern in Pester v4 is to use ",(0,i.jsx)(n.code,{children:"foreach"})," to generated tests and blocks based on external data, for example like this:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-powershell",children:'BeforeAll {\n # check all script files\n $files = Get-ChildItem *.ps1\n}\n\nforeach ($file in $files) {\n Describe "$file is correct" {\n It "has empty line at end" {\n # ...\n }\n\n It "has UTF-8 encoding" {\n # ...\n }\n }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["In this case the ",(0,i.jsx)(n.code,{children:"foreach"})," is evaluated during ",(0,i.jsx)(n.code,{children:"Discovery"})," because we simply run the whole script file. But ",(0,i.jsx)(n.code,{children:"BeforeAll"})," won't be invoked until ",(0,i.jsx)(n.code,{children:"Run"}),", so the ",(0,i.jsx)(n.code,{children:"$files"})," variable is not defined during ",(0,i.jsx)(n.code,{children:"Discovery"})," and the tests are not generated. This can be solved by defining the code that is used to generate tests into ",(0,i.jsx)(n.code,{children:"BeforeDiscovery"})," block. See ",(0,i.jsx)(n.a,{href:"data-driven-tests#beforediscovery",children:"Data driven tests"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"variables-are-not-available-in-test",children:"Variables are not available in test"}),"\n",(0,i.jsxs)(n.p,{children:["The example above has one more problem, if we move the setup to ",(0,i.jsx)(n.code,{children:"BeforeDiscovery"}),", we will generate the tests correctly, and the ",(0,i.jsx)(n.code,{children:"foreach"})," will define ",(0,i.jsx)(n.code,{children:"$file"})," variable. But we won't be able to use the ",(0,i.jsx)(n.code,{children:"$file"})," variable in the ",(0,i.jsx)(n.code,{children:"It"}),". We need to attach it to the ",(0,i.jsx)(n.code,{children:"It"})," using ",(0,i.jsx)(n.code,{children:"-TestCases"})," with a single test case:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-powershell",children:'BeforeDiscovery { # <- this runs in the Discovery phase, before the tests below are generated\n # check all script files\n $files = Get-ChildItem *.ps1\n}\n\nforeach ($file in $files) {\n Describe "$file is correct" {\n It "has empty line at end" -TestCases @{ File = $file } { # <- we pass $file data to the test\n # ...\n }\n\n It "has UTF-8 encoding" {\n # ...\n }\n }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["Both of the problems above are avoidable by using ",(0,i.jsx)(n.code,{children:"-ForEach"}),", see ",(0,i.jsx)(n.a,{href:"data-driven-tests",children:"Data driven tests"}),"."]})]})}function u(e={}){let{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},7008(e,n,s){s.d(n,{R:()=>o,x:()=>l});var t=s(1763);let i={},r=t.createContext(i);function o(e){let n=t.useContext(r);return t.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:o(e.components),t.createElement(r.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.