I have some old Pester v4 tests that are failing when I try to run them in Pester v5.
With Pester v4 I read that once Mock
is called it will continue to apply for the rest of the Describe
block, even if it was called within an It
block. So I would call it from the Describe
block, outside of an It
block, to make it obvious it didn't just apply to a single It
block.
That behaviour which worked in Pester v4 doesn't seem to work in Pester v5.
This is the script I'm writing tests against, MyScriptToTest.ps1:
function Get-FirstText ()
{
return 'Some text'
}
function Get-SecondText ()
{
return 'Other text'
}
function Get-Text ()
{
$a = Get-FirstText
$b = Get-SecondText
return "$a; $b"
}
And here are the tests, in MyScriptToTest.Tests.ps1:
BeforeAll {
. (Join-Path $PSScriptRoot 'MyScriptToTest.ps1')
}
Describe 'Get-FirstText' {
It 'returns text "Some text"' {
$result = Get-FirstText
$result | Should -Be 'Some text'
}
}
Describe 'Get-Text' {
It 'returns text "Some text; Other text"' {
Get-Text | Should -Be 'Some text; Other text'
}
Mock Get-FirstText { return 'Mock text' }
It 'returns text "Mock text; Other text" after mocking Get-FirstText' {
Get-Text | Should -Be 'Mock text; Other text'
}
}
The Pester v5 test output is as follows:
Describing Get-FirstText
[+] returns text "Some text" when called indirectly 13ms (8ms|5ms)
Describing Get-Text
[+] returns text "Some text; Other text" 66ms (54ms|13ms)
[-] returns text "Mock text; Other text" after mocking Get-FirstText 42ms (41ms|1ms)
Expected strings to be the same, but they were different.
String lengths are both 21.
Strings differ at index 0.
Expected: 'Mock text; Other text'
But was: 'Some text; Other text'
^
at Get-Text | Should -Be 'Mock text; Other text', C:\...\MyScriptToTest.Tests.ps1:20
at <ScriptBlock>, C:\...\MyScriptToTest.Tests.ps1:20
Tests completed in 565ms
Tests Passed: 2, Failed: 1, Skipped: 0 NotRun: 0
However, if I move the Mock
command into the It
block then it works:
BeforeAll {
. (Join-Path $PSScriptRoot 'MyScriptToTest.ps1')
}
Describe 'Get-FirstText' {
It 'returns text "Some text"' {
$result = Get-FirstText
$result | Should -Be 'Some text'
}
}
Describe 'Get-Text' {
It 'returns text "Some text; Other text"' {
Get-Text | Should -Be 'Some text; Other text'
}
It 'returns text "Mock text; Other text" after mocking Get-FirstText' {
Mock Get-FirstText { return 'Mock text' }
Get-Text | Should -Be 'Mock text; Other text'
}
}
I haven't figured out the new constraints around calling the Mock
command in Pester v5. Can the Mock
command still be called within a Describe
or Context
block, outside of an It
block? Or does it now always have to be called within an It
block?
The docs describe the v5+ change of where Mock
commands must (now) be placed (emphasis added):
Mocks are no longer effective in the whole
Describe
/Context
in which they were placed. Instead they will default to the block in which they were placed.
That is, a Mock
command placed directly inside Describe
or Context
commands - as in the code in your question - is now effectively ignored.
To spell it out: In Pester v5+, for a Mock
statement to be effective, it must be placed inside one of the following constructs:
Inside a BeforeAll
command, which therefore applies to all tests in the same scope.
BeforeAll
command may be placed directly inside a Describe
command or - for more fine-grained control - inside a nested Context
command.Inside an individual It
command, in which case it applies to that test only.
In the context of your code, this means:
Context
-level Mock
:
Describe 'Get-Text' {
It 'returns text "Some text; Other text"' {
Get-Text | Should -Be 'Some text; Other text'
}
Context 'With Mock' {
BeforeAll {
# Placing the Mock here works, and makes it take effect
# for all tests in this context.
Mock Get-FirstText { return 'Mock text' }
}
It 'returns text "Mock text; Other text" after mocking Get-FirstText' {
Get-Text | Should -Be 'Mock text; Other text'
}
# ... potentially more `It` commands to which the Mock applies.
}
}
It
-level Mock
:
Describe 'Get-Text' {
It 'returns text "Some text; Other text"' {
Get-Text | Should -Be 'Some text; Other text'
}
It 'returns text "Mock text; Other text" after mocking Get-FirstText' {
# Placing the Mock here works, and makes it take effect
# for this specific test only.
Mock Get-FirstText { return 'Mock text' }
Get-Text | Should -Be 'Mock text; Other text'
}
}