en-US/about_Mocking.help.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
TOPIC
    Mocking
 
SYNOPSIS
    Pester provides a set of Mocking functions making it easy to fake dependencies
    and also to verify behavior. Using these mocking functions can allow you to
    "shim" a data layer or mock other complex functions that already have their
    own tests.
 
DESCRIPTION
    With the set of Mocking functions that Pester exposes, one can:
 
    - Mock the behavior of ANY powershell command.
    - Verify that specific commands were (or were not) called.
    - Verify the number of times a command was called with a set of specified
     parameters.
 
MOCKING FUNCTIONS
    See Get-Help for any of the below functions for more detailed information.
 
    Mock
        Mocks the behavior of an existing command with an alternate
        implementation.
 
    Assert-VerifiableMocks
        Checks if any Verifiable Mock has not been invoked. If so, this will
        throw an exception.
 
    Assert-MockCalled
        Checks if a Mocked command has been called a certain number of times
        and throws an exception if it has not.
 
EXAMPLE
  function Build ($version) {
    Write-Host "a build was run for version: $version"
  }
 
  function BuildIfChanged {
    $thisVersion = Get-Version
    $nextVersion = Get-NextVersion
    if ($thisVersion -ne $nextVersion) { Build $nextVersion }
    return $nextVersion
  }
 
  $here = Split-Path -Parent $MyInvocation.MyCommand.Path
  $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".")
  . "$here\$sut"
 
  Describe "BuildIfChanged" {
    Context "When there are Changes" {
      Mock Get-Version {return 1.1}
      Mock Get-NextVersion {return 1.2}
      Mock Build {} -Verifiable -ParameterFilter {$version -eq 1.2}
 
      $result = BuildIfChanged
 
      It "Builds the next version" {
        Assert-VerifiableMocks
      }
      It "returns the next version number" {
        $result | Should Be 1.2
      }
    }
    Context "When there are no Changes" {
      Mock Get-Version { return 1.1 }
      Mock Get-NextVersion { return 1.1 }
      Mock Build {}
 
      $result = BuildIfChanged
 
      It "Should not build the next version" {
        Assert-MockCalled Build -Times 0 -ParameterFilter {$version -eq 1.1}
      }
    }
  }
 
MOCKING CALLS TO COMMANDS MADE FROM INSIDE SCRIPT MODULES
 
Let's say you have code like this inside a script module (.psm1 file):
 
  function BuildIfChanged {
    $thisVersion = Get-Version
    $nextVersion = Get-NextVersion
    if ($thisVersion -ne $nextVersion) { Build $nextVersion }
    return $nextVersion
  }
 
  function Build ($version) {
    Write-Host "a build was run for version: $version"
  }
 
  # Actual definitions of Get-Version and Get-NextVersion are not shown here,
  # since we'll just be mocking them anyway. However, the commands do need to
  # exist in order to be mocked, so we'll stick dummy functions here
 
  function Get-Version { return 0 }
  function Get-NextVersion { return 0 }
 
  Export-ModuleMember -Function BuildIfChanged
 
You wish to write a unit test for this module which mocks the calls to Get-Version
and Get-NextVersion from the module's BuildIfChanged command. In older versions of
Pester, this was not possible. As of version 3.0, there are two ways you can perform
unit tests of PowerShell script modules. The first is to inject mocks into a module:
 
For these example, we'll assume that the PSM1 file is named "MyModule.psm1", and that
it is installed on your PSModulePath.
 
  Import-Module MyModule
 
  Describe "BuildIfChanged" {
    Context "When there are Changes" {
      Mock -ModuleName MyModule Get-Version { return 1.1 }
      Mock -ModuleName MyModule Get-NextVersion { return 1.2 }
 
      # Just for giggles, we'll also mock Write-Host here, to demonstrate that you can
      # mock calls to commands other than functions defined within the same module.
      Mock -ModuleName MyModule Write-Host {} -Verifiable -ParameterFilter {
        $Object -eq 'a build was run for version: 1.2'
      }
 
      $result = BuildIfChanged
 
      It "Builds the next version and calls Write-Host" {
        Assert-VerifiableMocks
      }
 
      It "returns the next version number" {
        $result | Should Be 1.2
      }
    }
 
    Context "When there are no Changes" {
      Mock -ModuleName MyModule Get-Version { return 1.1 }
      Mock -ModuleName MyModule Get-NextVersion { return 1.1 }
      Mock -ModuleName MyModule Build { }
 
      $result = BuildIfChanged
 
      It "Should not build the next version" {
        Assert-MockCalled Build -ModuleName MyModule -Times 0 -ParameterFilter {
          $version -eq 1.1
        }
      }
    }
  }
 
Notice that in this example test script, all calls to Mock and Assert-MockCalled have had the
-ModuleName MyModule parameter added. This tells Pester to inject the mock into the module's scope,
which causes any calls to those commands from inside the module to execute the mock instead.
 
When you write your test script this way, you can mock commands that are called by the module's
internal functions. However, your test script is still limited to accessing the public, exported
members of the module. If you wanted to write a unit test that calls Build directly, for example,
it wouldn't work using the above technique. That's where the second approach to script module testing
comes into play. With Pester 3.0's InModuleScope command, you can cause entire sections of your test
script to execute inside the targeted script module. This gives you access to non-exported members of
the module. For example:
 
  Import-Module MyModule
 
  Describe "Unit testing the module's internal Build function:" {
    InModuleScope MyModule {
      $testVersion = 5.0
      Mock Write-Host { }
 
      Build $testVersion
 
      It 'Outputs the correct message' {
        Assert-MockCalled Write-Host -ParameterFilter {
          $Object -eq "a build was run for version: $testVersion"
        }
      }
    }
  }
 
Notice that when using InModuleScope, you no longer need to specify a -ModuleName parameter when calling
Mock or Assert-MockCalled for commands within that module. You are also able to directly call the Build
function, which the module does not export.
 
SEE ALSO
    Mock
    Assert-VerifiableMocks
    Assert-MockCalled
    InModuleScope
    Describe
    Context
    It