{"id":1898,"date":"2014-11-04T00:00:00","date_gmt":"2014-11-04T00:00:00","guid":{"rendered":"https:\/\/test.simple-talk.com\/uncategorized\/practical-powershell-unit-testing-mock-objects\/"},"modified":"2017-09-26T10:03:27","modified_gmt":"2017-09-26T10:03:27","slug":"practical-powershell-unit-testing-mock-objects","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/sysadmin\/powershell\/practical-powershell-unit-testing-mock-objects\/","title":{"rendered":"Practical PowerShell Unit-Testing: Mock Objects"},"content":{"rendered":"<div id=\"pretty\">\n<ul>\n<li>\n<div class=\"float-left\">\u00a0<\/div>\n<div class=\"float-left\"><a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/practical-powershell-unit-testing-getting-started\/\">PowerShell Unit-Testing: Getting Started<\/a><\/div>\n<\/li>\n<li>\n<div class=\"float-left\">\u25ba<\/div>\n<div class=\"float-left\">Practical PowerShell Unit-Testing: Mock Objects<\/div>\n<\/li>\n<li>\n<div class=\"float-left\">\u00a0<\/div>\n<div class=\"float-left\"><a href=\"https:\/\/www.simple-talk.com\/sysadmin\/powershell\/practical-powershell-unit-testing-checking-program-flow\/\">Practical PowerShell Unit-Testing: Checking program flow<\/a><\/div>\n<\/li>\n<\/ul>\n<h2 id=\"first\">Contents<\/h2>\n<ul>\n<li><a href=\"#second\">Mocking.<\/a>\n<ul>\n<li><a href=\"#third\">Multiple Mocks Example.<\/a><\/li>\n<li><a href=\"#fourth\">Logic-Added Mock Example.<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#fifth\">Parameterized Test Cases.<\/a>\n<ul>\n<li><a href=\"#sixth\">Simple Parameterized Test Example.<\/a><\/li>\n<li><a href=\"#seventh\">Parameterized Test Example with ContextUsing.<\/a><\/li>\n<li><a href=\"#eighth\">Get-TextFileNames Example Revisited.<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a href=\"#ninth\">Conclusion.<\/a><\/li>\n<\/ul>\n<p>In the previous installment, you saw how to install and use Pester, and the basic structure of unit tests in PowerShell. This article covers mocking and parameterized test cases. The third installment will conclude with data and program flow validation.<\/p>\n<h2 id=\"second\">Mocking<\/h2>\n<p>Mock objects, or &#8216;mocks&#8217;, are objects that are deliberately created to simulate other objects as simply as possible.\u00a0 They \u00a0are frequently used in unit-testing to mimic the behavior of real objects in controlled ways, when it is impractical or time-consuming to use the real object. \u00a0Pester creates mocks with the <code>Mock<\/code> command, which has a very simple syntax yet provides a lot of flexibility at the same time. Let&#8217;s see how it works.<\/p>\n<p>Say you want to create a simple function that returns a list of file names in the current directory that are text files (i.e. those with a .txt suffix). The first test you might write is to see if it can return a single such file:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribe 'Get-TextFileNames' {\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFiles | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t}  \r\n\t<\/pre>\n<p>You might write the code for<code> Get-TextFileNames<\/code> like this:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tfunction Get-TextFileNames() {\r\n\t\u00a0\u00a0\u00a0 Get-ChildItem | Where Name -like *.txt | Select -expand Name\r\n\t}  \r\n\t<\/pre>\n<p>That is, use <code>Get-ChildItem<\/code> to list the files in the current directory, filter the list to those ending in <code>.txt<\/code>, and from the remaining items-a list of <code>FileInfo<\/code> objects-return just the name.<\/p>\n<p>Run that test and it will fail (unless you happen to have a file called <code>a923e023.txt<\/code>-and no other text files-in your directory. With no text files in your directory, you would see this:<\/p>\n<pre class=\"lang:ps theme:powershell-output\">\tPS&gt; Invoke-Pester\r\n\tExecuting all tests in 'C:\\usr\\tmp'\r\n\tDescribing Get-TextFileNames\r\n\t\u00a0[-] returns text files 346ms\r\n\t\u00a0\u00a0 Expected: {a923e023.txt}\r\n\t\u00a0\u00a0 But was:\u00a0 {}  \r\n<\/pre>\n<p>You can immediately see that it is a fragile test indeed that happens to depend on the contents of your current directory. Even if you create that text file here for the sake of the test, the next time you want to run your test you might be in a different directory. Or perhaps a colleague wants to run the test on his\/her machine. Or you want your build machine that supports your continuous integration to run it. The goal, then, is to isolate tests from outside influences-like your file system. This is a great use case for mocks.<\/p>\n<p>For this test, then, we want to mock the <code>Get-ChildItem<\/code> cmdlet so that it returns a single <code>FileInfo<\/code> object representing a file named <code>a923e023.txt. <\/code>To do this we use the <code>Mock<\/code> command, indicating the command we want to mock and what we want it to return rather than calling the real <code>Get-ChildItem<\/code>:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tMock\r\n\t\u00a0\u00a0\u00a0 -CommandName Get-ChildItem\r\n\t\u00a0\u00a0\u00a0 -MockWith { [PSCustomObject]@{ Name = 'a923e023.txt' } }  \r\n<\/pre>\n<p>The <code>MockWith<\/code> argument is a PowerShell script block, i.e. just a set of statements surrounded by braces. In this case, we have a single statement:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t[PSCustomObject]@{ Name = 'a923e023.txt' }<\/pre>\n<p>This is a concise way to construct a PowerShell object that looks sufficiently like a particular real object for our needs. In this case, we are creating an object with a single <code>Name<\/code> property, because that is precisely the property that our code is using; here it is again:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2084-img11F.jpg\" alt=\"2084-img11F.jpg\" \/><\/p>\n<p>Now let&#8217;s put that mock into the test shown earlier (here I have abbreviated the command by omitting the parameter names <code>-CommandName<\/code> and <code>-MockWith<\/code>).<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribe 'Get-TextFileNames' {\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { [PSCustomObject]@{ Name = 'a923e023.txt' } }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t}\r\n\t<\/pre>\n<p>This test will now pass no matter what directory you are in or what machine you are on. We have completely isolated the test from the real <code>Get-ChildItem<\/code> and thus from the real file system.<\/p>\n<p>This test might seem somewhat pointless, though, because I fed it the precise input I am attempting to validate against. But that is not the case! This is a real test and a useful one. It validates that the current implementation of <code>Get-TextFileNames<\/code> is working (for this one set of data). Here are just a couple variations of the implementation where that test would have failed:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2084-img120.jpg\" alt=\"2084-img120.jpg\" \/><\/p>\n<p>By controlling for <code>Get-ChildItem<\/code> (with a mock) we are allowing the test to validate the remaining cmdlets within <code>Get-TextFileNames<\/code>. Let&#8217;s really give <code>Get-TextFileNames<\/code> a workout by expanding our test coverage a bit to see how robust it is. Using the same simple mock, returning different sets of data, we can mimic a real directory that could have varied contents. I have included the test above as the first test here, but expanded its description to make it clear what that test is validating.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribe 'Get-TextFileNames' {\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file when that is all there is' {\r\n\t\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0Mock Get-ChildItem {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0[PSCustomObject]@{ Name = 'a923e023.txt' }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file when there are assorted files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'a923e023.txt' },\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'wlke93jw3.doc' }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns multiple text files amongst assorted files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'a923e023.txt' },\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'wlke93jw3.doc' },\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'ke923jd.txt' },\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'qq02000.doc' }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be ('a923e023.txt','ke923jd.txt')\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns nothing when there are no text files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'wlke93jw3.doc' },\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ Name = 'qq02000.doc' }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should BeNullOrEmpty\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t}  \r\n<\/pre>\n<p>There are several more tests I would include, even for a function as simple as this, but I have kept the list short for this article. The above is a perfectly valid set of tests you might use, but in practice, I would use the power of PowerShell to streamline that a bit. First, introduce a helper function, a mini-factory, to create my data set. Here I am taking in a list of names and generating <code>FileInfo<\/code>-like objects, including both the <code>Name<\/code> property that you have seen in the above tests, and the <code>FullName<\/code> property, which you might use in additional tests.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tfunction CreateFileList([string[]]$names)\r\n\t{\r\n\t\u00a0\u00a0\u00a0 $names | ForEach {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ FullName = \"c:\\foo\\bar\\$_\"; Name = $_; }\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t}  \r\n\t<\/pre>\n<p>With that helper function added to the test file, the above set of tests can now be written more concisely as:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribe 'Get-TextFileNames' {\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file when that is all there is' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $myList = 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { CreateFileList $myList }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns one text file when there are assorted files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $myList = 'a923e023.txt','wlke93jw3.doc'\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { CreateFileList $myList }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be 'a923e023.txt'\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns multiple text files amongst assorted files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $myList = 'a923e023.txt','wlke93jw3.doc','ke923jd.txt','qq02000.doc'\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { CreateFileList $myList }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be ('a923e023.txt','ke923jd.txt')\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 It 'returns nothing when there are no text files' {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 $myList = 'wlke93jw3.doc','qq02000.doc'\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { CreateFileList $myList }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should BeNullOrEmpty\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0\r\n\t}  \r\n<\/pre>\n<p>Using the <code>CreateFileList<\/code> mini-factory function, we reduced the suite of unit tests by about one-third. After introducing a few more Pester concepts, you will see a further substantial reduction for this same group of tests.<\/p>\n<p>Mocking gets particularly interesting when you also add the <code>ParameterFilter<\/code> and\/or use multiple mocks, discussed shortly.<\/p>\n<p>But first, I want to mention the potential for abuse with mocks. This is not peculiar to PowerShell, but it certainly applies to PowerShell now that you have mocking capability with Pester. The issue is that of <i>state<\/i>-based testing (testing the result of a method) vs. <i>behavior<\/i>-based testing (testing what happened during a method call). While there are arguments for both sides of the issue, I believe that judicious use of both techniques provides the best results. Often the code itself tells you what kind of test might be a better fit: if you have a method that generates no side effects and returns a value, then clearly state-based testing is better. For a method that has side effects and returns no result, then clearly behavior-based testing is appropriate.\u00a0The problems with behavior-based testing are nicely summarized by Matt Wrock in his post <a href=\"http:\/\/www.hurryupandwait.io\/blog\/unit-testing-powershell-and-hello-pester\">Unit Testing PowerShell and Hello Pester<\/a>:<\/p>\n<p><i>&#8220;We are reaching deeper into the function and assuming we know more that we should about what the function is doing. We are testing the implementation details of the function rather that the outcome of its state. The behavior based tests open the testing framework up to added fragility since changes in implementation often requires changing the tests. Mocking can lead developers to overusing behavior based tests since it makes it so easy to test the function&#8217;s interactions at various points of the function&#8217;s logic.&#8221;<\/i><\/p>\n<p>PowerShell, as Wrock further points out, is perhaps more challenging to do state-based testing just due to the nature of the &#8220;PowerShell ecosystem&#8221; (Wrock&#8217;s phrase, which I think is particularly apropos). With my own explorations, thus far, I aim for state-based testing, but I still find I need to use behavior-based testing in many cases.<\/p>\n<h3 id=\"third\">Multiple Mocks Example<\/h3>\n<p>Here I am setting up two mocks for the <i>same<\/i> cmdlet, <code>Select-String<\/code>. The first mock introduces the <code>ParameterFilter<\/code> argument that lets you limit the set of inputs for which a mock will trigger. Here it is rather selective; it will be triggered only for values of <code>$Path <\/code>sent to <code>Select-String<\/code> that match the filter (which is a regular expression specifying to match anything ending in B, D, or E).<\/p>\n<p>There are two key things to know when using multiple mocks. First, mocks are examined for triggering in the order you define them. Second, only one mock gets triggered for a given <code>CommandName <\/code>value. Therefore, because the unconditional mock occurs <i>after<\/i> the more selective mock, the second mock is a &#8220;catch-all&#8221;; anything that the first mock chose to ignore will trigger the second mock.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t\u00a0\u00a0\u00a0 $filter = '(B|D|E)$'\r\n\t\u00a0\u00a0\u00a0 Mock Select-String { \"matched!\" } -ParameterFilter { $Path -match $filter }\r\n\t\u00a0\u00a0\u00a0 Mock Select-String \r\n\t<\/pre>\n<p>Thus, the first mock will trigger for &#8220;fileB&#8221;, returning &#8220;matched!&#8221;, while for an input of &#8220;fileA&#8221; the second mock will trigger, returning nothing (because the <code>MockWith<\/code> parameter is not specified). Extend that with more cases and you have successfully emulated a standard <code>switch<\/code> statement.<\/p>\n<h3 id=\"fourth\">Logic-Added Mock Example<\/h3>\n<p>The logic in this next example is just another way to produce results identical to the previous example. Here, instead of using multiple mocks and a <code>ParameterFilter<\/code>, though, it uses additional logic within a single mock script block.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\t\u00a0\u00a0\u00a0 $filter = '(B|D|E)$'\r\n\t\u00a0\u00a0 \u00a0Mock Select-String {\r\n\t\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0if ($Path -match $filter) { \"matches found\" }\r\n\t\u00a0\u00a0\u00a0 }\r\n\t<\/pre>\n<p>The <code>MockWith<\/code> parameter is just a standard PowerShell script block, so you may include whatever logic you wish. And here, you could even use a standard <code>switch<\/code> statement, if you had more cases to handle.<\/p>\n<p>Now let&#8217;s put it all together. Here is a function under test (<code>GetFilesLackingItem<\/code>) and a single unit test for it. The function returns the names of files that do not contain a line having the specified pattern. It uses the standard PowerShell cmdlets <code>Get-ChildItem<\/code> and <code>Select-String<\/code> to do most of the work, then decorates them with some simple manipulations. <i>It is those manipulations that need to be tested<\/i> rather than the functionality of either cmdlet. Thus, the test sets up mocks for both <code>Select-String<\/code> and <code>Get-ChildItem<\/code>. You will see the data generation technique described just above used here to provide mock data for <code>Get-ChildItem<\/code>. You will also see the multiple-mock technique described above to provide intelligence to the mock for <code>Select-String<\/code>. (You can try out this code by copying both source and test into a single Demo.Tests.ps1 file and then running <code>Invoke-Pester<\/code> on the containing directory.)<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tfunction GetFilesLackingItem([string]$pattern)\r\n\t{\r\n\t\u00a0 $filesFound = Get-ChildItem -r *.csproj |\r\n\t\u00a0\u00a0\u00a0 ? { !( Select-String -pattern $pattern -path $_.FullName ) }\r\n\t\u00a0 if ($filesFound) { $filesFound.Name } else { @() }\r\n\t}\r\n\t\u00a0\r\n\tDescribe \"GetFilesLackingItem\" {\r\n\t\u00a0 Context \"checks some files\" {\r\n\t\u00a0\u00a0\u00a0 It \"reports subset of files missing item\" {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $fileList = \"nameA\", \"nameB\", \"nameC\", \"nameD\", \"nameE\" | % {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 [PSCustomObject]@{ FullName = $_; Name = $_; }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { return $fileList }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $filter = '(B|D|E)$'\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Select-String { \"matches found\" }\u00a0 -param { $Path -match $filter }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Select-String \r\n\t\u00a0  \r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $result = GetFilesLackingItem \"dummy\"\r\n\t\u00a0  \r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $result.Count | Should Be ($fileList.Name -notmatch $filter).Count\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0 }\r\n\t}\r\n\t<\/pre>\n<p><\/p>\n<div class=\"theory\"><img decoding=\"async\" class=\"float-left\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2084-pencil.jpg\" alt=\"2084-pencil.jpg\" \/><\/p>\n<p><i>Use one or more mocks to isolate your unit under test.<\/i><\/p>\n<\/div>\n<h2 id=\"fifth\">Parameterized Test Cases<\/h2>\n<p>A parameterized test case is a simple but powerful testing technique, but surprisingly unknown enough to not even have spawned a Wikipedia entry! It is really as straightforward as it sounds: a test case that takes parameters. It is analogous to a function. Consider first this function without parameters:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tfunction f()\r\n\t{\r\n\t\u00a0\u00a0\u00a0 return 5 + 23;\r\n\t}\r\n\t<\/pre>\n<p>When you invoke f() you always get the sum of 5 and 23. Quite&#8230; unexciting. Now jazz that up with parameters creating-you guessed it-a parameterized function:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tfunction f(int a, int b)\r\n\t{\r\n\t\u00a0\u00a0\u00a0 return a + b;\r\n\t}\r\n\t<\/pre>\n<p>Now you can still get 28 by invoking f(5, 23) but having parameterized your inputs you now have the freedom to\u00a0 do f(0, -123), f(42, 42), and so on ad infinitum.<\/p>\n<p>A parameterized <i>test<\/i> serves exactly the same purpose. Say you write a regular test case that validates some function f with 1 feinberger returns 2 ounces of quadrotriticale. Then you write another test that validates f with 2 feinbergers returns 3 ounces of quadrotriticale. This second test is virtually identical to the first, except for the size of the input and the resultant size of the output. To eliminate all the duplicated code, you just have to feed sets of input\/output parameters to a parameterized version of the test function.<\/p>\n<p>You can find support for parameterized test cases in NUnit for .NET but Pester does not have native support for this&#8230; yet! I submitted a <a href=\"https:\/\/github.com\/pester\/Pester\/issues\/179\">feature request for this<\/a> recently and the Pester crew responded readily to adding native support in the near future. But you do not have to wait; due to the nature of PowerShell it is elegantly simple to do this yourself.<\/p>\n<h3 id=\"sixth\">Simple Parameterized Test Example<\/h3>\n<p>First, wrap your test inside a function. The function signature should include all the input and output elements you wish to parameterize and, of course, the body of the function should use those parameters (the <code>Test-SafeProperties<\/code> function in the code below). Next specify each set of inputs and outputs to feed to the function as an array whose elements are ordered to match the parameter order of the function signature. Add as many data sets (tests) to this list as you wish (the <code>$dataSets<\/code> variable in the code). Finally, marry the two together by piping the data to the function applying PowerShell&#8217;s <i>splatting<\/i> functionality (see <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/jj672955.aspx\">about_splatting<\/a>).<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tContext \"for deep property failure\" {\r\n\t\u00a0\r\n\t\u00a0 # wrap the test in a function\r\n\t\u00a0 function Test-SafeProperties($parent, $leaf, $description, $defaultValue = \"\")\r\n\t\u00a0 {\r\n\t\u00a0\u00a0\u00a0 It \"returns default when $description\" {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $result = Get-SafeProperty $testItem $parent,$leaf $defaultValue\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 $result | Should Be $defaultValue\r\n\t\u00a0\u00a0\u00a0 }\r\n\t\u00a0 }\r\n\t\u00a0\r\n\t\u00a0 # define multiple sets of test data\r\n\t\u00a0 $dataSets = (\r\n\t\u00a0\u00a0\u00a0 (\"BaseItem\", \"\", \"no leaf specified\"),\r\n\t\u00a0\u00a0\u00a0 (\"BaseItem\", \"NonExistent\", \"non-existent leaf specified\", \"none\"),\r\n\t\u00a0\u00a0\u00a0 (\"NonExistent\", \"ChildItem\", \"non-existent parent specified\", 25)\r\n\t\u00a0 )\r\n\t\u00a0\r\n\t\u00a0 # feed the data to the test function to run all tests\r\n\t\u00a0\u00a0$dataSets | % { Test-SafeProperties @_ }\r\n\t}\u00a0\r\n\t<\/pre>\n<h3 id=\"seventh\">Parameterized Test Example with ContextUsing<\/h3>\n<p>The example above actually provides a complete, viable technique to do parameterized tests. However, I try to aspire to the <a href=\"http:\/\/threevirtues.com\/\">three great virtues<\/a> of a programmer (as espoused by Larry Wall, the author of Perl); in this case, laziness is particularly apropos. To be able to write a more concise version of the above code that performs equivalently, I wrote an extension for Pester&#8217;s <code>Context<\/code> command called <code>ContextUsing<\/code>. The original syntax of the <code>Context<\/code> command is:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tContext [-Name] &lt;String&gt; [-Fixture] &lt;ScriptBlock&gt; <\/pre>\n<p>The new \u00a0<code>ContextUsing<\/code> command adds the <code>TestCases<\/code> parameter:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tContextUsing [-Name] &lt;String&gt; [-TestCases] &lt;Object[][]&gt; [-Fixture] &lt;ScriptBlock&gt;  <\/pre>\n<p>The code below shows one example usage. Notice that the script block must start with a <code>param()<\/code> statement that enumerates the parameters of the script block (or <i>anonymous function<\/i> if you prefer). Each test case, then, must specify values for each of those parameters.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tContextUsing \"for deep property failure\" (\r\n\t\u00a0 (\"BaseItem\", \"\", \"no leaf specified\"),\r\n\t\u00a0 (\"BaseItem\", \"NonExistent\", \"non-existent leaf specified\", \"none\"),\r\n\t\u00a0 (\"NonExistent\", \"ChildItem\", \"non-existent parent specified\", 25)\r\n\t) {\r\n\t\u00a0 param($parent, $leaf, $description, $defaultValue = \"\")\r\n\t\u00a0\r\n\t\u00a0 It \"returns default when $description\" {\r\n\t\u00a0\u00a0\u00a0 $result = Get-SafeProperty $testItem $parent,$leaf $defaultValue\r\n\t\u00a0\u00a0\u00a0 $result | Should Be $defaultValue\r\n\t\u00a0 }\r\n\t}\r\n\t\u00a0\r\n<\/pre>\n<p><\/p>\n<div class=\"theory\"><img decoding=\"async\" class=\"float-left\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/imported\/2084-pencil.jpg\" alt=\"2084-pencil.jpg\" \/><\/p>\n<p><i>Use a parameterized test to avoid massive code duplication.<\/i><\/p>\n<\/div>\n<p>\u00a0<i>Note 1:<\/i> Just as the Test Anatomy section in part 1 warned you about placement of opening braces for script blocks, the opening parenthesis of the test case list here must start on the same line as the <code>ContextUsing<\/code> as shown, and the closing parenthesis must be on the same line as the opening brace of the script block.<\/p>\n<p><i>Note 2:<\/i> A handy technique is to add a parameter (<code>$description <\/code>in the above code) that rather than being <i>used by<\/i> the test for validation just <i>describes<\/i> the test. That parameter is used in the description of the <code>It<\/code> command, and is displayed in your test output.<\/p>\n<p><i>Note 3:<\/i> To add <code>ContextUsing<\/code> to your Pester distribution, simply copy the ContextUsing.ps1 file (attached to this article) into your Pester distribution, then re-import the Pester module into your PowerShell window (i.e. use <code>Import-Module<\/code> with the <code>-Force<\/code> parameter).<\/p>\n<p><i>Note 4:<\/i> <code>ContextUsing<\/code> does <i>not<\/i> support passing only a single test case. That is, there must be at least <i>two<\/i> sets of data supplied to the <code>TestCases<\/code> parameter. This is not really a limitation per se, because if you only have one test case, you do not need <code>ContextUsing<\/code>!<\/p>\n<h3 id=\"eighth\">Get-TextFileNames Example Revisited<\/h3>\n<p>As promised earlier, let&#8217;s go back to the <code>Get-TextFileNames<\/code> example at the top of this article and see how, with parameterized tests you can achieve yet another dramatic reduction in line count. If you look back at the 4 tests for <code>Get-TextFileNames<\/code>, you will observe that all four tests are exactly the same except for the list of files being fed into the mock and the result that we expect to get back from the function under test. In this case, it is only a couple lines of code but you can imagine that more involved tests with perhaps a dozen or two dozen lines would cause a tremendous amount of duplicated code. Parameterized tests, as you have seen, eliminate this code duplication. Without further ado, here is the set of 4 test for <code>Get-TextFileNames<\/code> rewritten as a set of parameterized tests. \u00a0I have spread out and commented the test data to make it easier to visually distinguish the arguments, but there are now only two lines of actionable code: one to define the mock and one to validate the function with the configured mock.<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribe 'Get-TextFileNames' {\r\n\t\u00a0\r\n\t\u00a0 ContextUsing \"file combinations\" (\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 # Data for test #1\r\n\t\u00a0\u00a0\u00a0 ('a923e023.txt', # the $files parameter\r\n\t\u00a0\u00a0\u00a0\u00a0 'a923e023.txt', # the $expectedResult parameter\r\n\t\u00a0\u00a0\u00a0\u00a0 'one text file when that is all there is'), # the description parameter\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 # Data for test #2\r\n\t\u00a0\u00a0\u00a0 (('a923e023.txt','wlke93jw3.doc'),\r\n\t\u00a0\u00a0\u00a0\u00a0 'a923e023.txt',\r\n\t\u00a0\u00a0\u00a0\u00a0 'one text file when there are assorted files'),\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 # Data for test #3\r\n\t\u00a0\u00a0\u00a0 (('a923e023.txt','wlke93jw3.doc','ke923jd.txt','qq02000.doc'),\r\n\t\u00a0\u00a0\u00a0\u00a0 ('a923e023.txt','ke923jd.txt'),\r\n\t\u00a0\u00a0\u00a0\u00a0 'multiple text files amongst assorted files'),\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0 # Data for test #4\r\n\t\u00a0\u00a0\u00a0 (('wlke93jw3.doc','qq02000.doc'),\r\n\t\u00a0\u00a0\u00a0\u00a0 $null,\r\n\t\u00a0\u00a0\u00a0\u00a0 'nothing when there are no text files')\r\n\t\u00a0\r\n\t\u00a0 ) {\r\n\t\u00a0\u00a0\u00a0 param($files, $expectedResult, $description)\r\n\t\u00a0\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 It \"returns $description\" {\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Mock Get-ChildItem { CreateFileList $files }\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Get-TextFileNames | Should Be $expectedResult\r\n\t\u00a0\u00a0\u00a0\u00a0\u00a0 }\r\n\t\u00a0 }\r\n\t}  \r\n<\/pre>\n<p>Note that only two of the three parameters are used in running the test: <code>$files<\/code> and <code>$expectedResult<\/code>. The third parameter (<code>$description<\/code>), as mentioned above, is for identifying the test. Run the tests and you will see that the description is valuable to have:<\/p>\n<pre class=\"lang:ps theme:powershell-ise\">\tDescribing Get-TextFileNames\r\n\t\u00a0\u00a0 Context file combinations\r\n\t\u00a0\u00a0\u00a0 [+] returns one text file when that is all there is 155ms\r\n\t\u00a0\u00a0\u00a0 [+] returns one text file when there are assorted files 34ms\r\n\t\u00a0\u00a0\u00a0 [+] returns multiple text files amongst assorted files 35ms\r\n\t\u00a0\u00a0\u00a0 [+] returns nothing when there are no text files 24ms  \r\n<\/pre>\n<h2 id=\"ninth\">Conclusion<\/h2>\n<p>This article showed how to make one test function actually cover multiple test cases; PowerShell makes that particularly easy to do. It also introduced mocks, which, as you have seen, make it easy to isolate components so you can focus on testing just your chunk of code without external influences. But mocks serve an additional purpose, providing hooks to validate program flow. The next installment explains how to do that, as well as validating data, providing some additional support to validate array data.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Pester allows you to automate the testing of PowerShell scripts. It can test the output of a function as you develop it by validating simple data (scalars) and  arrays, Pester allows you to  focus on the one function you want to test by using &#8216;mocking&#8217; to fake all the other functions and Cmdlets, It also uses Parameterized tests save you from writing essentially the same test over and over with just a different input valu&hellip;<\/p>\n","protected":false},"author":221868,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[35],"tags":[4635,4871],"coauthors":[6802],"class_list":["post-1898","post","type-post","status-publish","format-standard","hentry","category-powershell","tag-powershell","tag-sysadmin"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1898","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221868"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=1898"}],"version-history":[{"count":7,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1898\/revisions"}],"predecessor-version":[{"id":72924,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/1898\/revisions\/72924"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=1898"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=1898"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=1898"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=1898"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}