-
-
Notifications
You must be signed in to change notification settings - Fork 473
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't Verify Mocks Called Within BeforeAll Block #1622
Comments
Still working through the idiosyncracies of v5 myself and would also like some clarity on scoping in Pester 5. Have seen some odd behaviour when mocking things that Pester uses internally. (Set-TraceLevel for example) I think the main issue is function Get-Foo() needs to be wrapped in Before block. Mocks are scoped based on their placement Here's a working example, including new Should-Invoke syntax. I added the function code within BeforeAll, also works in BeforeEach. Describe 'Example' {
BeforeAll {
function Get-Foo() {}
Mock Get-Foo -Verifiable {}
}
Context 'BeforeAll' {
BeforeAll {
function Get-Foo() {}
Mock Get-Foo -Verifiable {}
}
It "Doesn't work?" {
Get-Foo
Should -Invoke Get-Foo -Times 1 -Exactly
}
}
Context 'BeforeEach' {
BeforeEach {
Get-Foo
}
It 'Does work' {
Should -Invoke Get-Foo -Times 1 -Exactly
}
}
Describe 'It' {
It 'Also works' {
Get-Foo
Should -Invoke Get-Foo -Times 1 -Exactly
}
}
} diff --git b/my.tests.ps1 a/my.tests.ps1
index 2ec38a7..9ad915f 100644
--- b/my.tests.ps1
+++ a/my.tests.ps1
@@ -1,15 +1,17 @@
-function Get-Foo() {}
Describe 'Example' {
- BeforeEach {
+ BeforeAll {
+ function Get-Foo() {}
Mock Get-Foo -Verifiable {}
}
Context 'BeforeAll' {
BeforeAll {
- Get-Foo
+ function Get-Foo() {}
+ Mock Get-Foo -Verifiable {}
}
It "Doesn't work?" {
- Assert-MockCalled Get-Foo -Times 1 -Exactly
+ Get-Foo
+ Should -Invoke Get-Foo -Times 1 -Exactly
}
}
Context 'BeforeEach' {
@@ -17,13 +19,13 @@ Describe 'Example' {
Get-Foo
}
It 'Does work' {
- Assert-MockCalled Get-Foo -Times 1 -Exactly
+ Should -Invoke Get-Foo -Times 1 -Exactly
}
}
Describe 'It' {
It 'Also works' {
Get-Foo
- Assert-MockCalled Get-Foo -Times 1 -Exactly
+ Should -Invoke Get-Foo -Times 1 -Exactly
}
}
}
BeforeAll: Defines a series of steps to perform at the beginning of the current Context or Describe block. Should documentation is missing new -Invoke syntax. https://pester.dev/docs/commands/Should From Converting tests to pester 5 Code inside a Describe- or Context-block should be inside a It-, Before-, or After*-block. The exception to this is variables that contain test cases, those variables should be placed inside the Describe-block and before the It-block, but outside of the Before*-blocks. they must be outside Before*-blocks and It-blocks, but inside Describe- or Context-blocks.* From Community talk about Pester 5
We can leverage the new PesterPreference / Configuration Object to get some additional details on what is going on. I have a setup script which configures the object's common properties. Here's one I toggle to show the details on what Pester's doing with mocks. $PesterPreference = [PesterConfiguration]::Default
$PesterPreference.Debug.WriteDebugMessages = $true |
@asears Thank you for the feedback. I agree that it seems to be a matter of scoping but I don't believe that it is related to where the "real" function is defined. Rather, it seems to be a matter of where the mocked function is invoked. From what I can see, the reason your code sample works and mine does not is because of where When I move the call to $ diff _a.ps1 _b.ps1 --unified
--- _a.ps1 2020-07-01 12:26:06.028298100 -0400
+++ _b.ps1 2020-07-01 13:34:14.675666500 -0400
@@ -8,9 +8,9 @@
BeforeAll {
function Get-Foo() {}
Mock Get-Foo -Verifiable {}
+ Get-Foo
}
It "Doesn't work?" {
- Get-Foo
Should -Invoke Get-Foo -Times 1 -Exactly
}
} Here's a mockup of the v3 code I was trying to migrate: function Remove-Foo {
foreach ($i in @('bar','baz')) {
if (Test-Path $i) {
Remove-Item $i
}
}
}
Describe 'Remove-Foo' {
# Make sure we don't accidentally remove anything
Mock Remove-Item {} -Verifiable
$vars = @('bar','baz')
Context 'Testing foo' {
Mock Test-Path { $false } -Verifiable
Remove-Foo
It 'Calls Test-Path exactly twice' {
Assert-MockCalled Test-Path -Times 2 -Exactly
}
foreach ($v in $vars) {
It "Should check for $v" {
Assert-MockCalled Test-Path -Times 1 -Exactly -ParameterFilter { $Path -eq $v}
}
}
}
Context 'Foo does not exist' {
Mock Test-Path { $false }
Remove-Foo
It 'Does not call Remove-Item' {
Assert-MockCalled Remove-Item -Times 0 -Exactly
}
}
Context 'Foo exists' {
Mock Test-Path { $true }
Remove-Foo
It 'Calls Remove-Item exactly twice' {
Assert-MockCalled Remove-Item -Times 2 -Exactly
}
foreach ($v in $vars) {
It "Should check for $v" {
Assert-MockCalled Remove-Item -Times 1 -Exactly -ParameterFilter { $Path -eq $v}
}
}
}
} Note that My tests have shown that the mocked function is getting called as expected when invoked from the Describe 'foo' {
BeforeAll {
[int]$callCount = 1
Mock Write-Host -Verifiable {}
Write-Host ''
Assert-MockCalled Write-Host -Times $callCount -Exactly
}
It 'bar' {
Assert-MockCalled Write-Host -Times $callCount -Exactly
}
} Output:
This shows that Changing Output:
From all of this I can infer:
The last point seems counter-intuitive to me so what I would like to understand is whether these 3 "facts" are actually true. I think the logic I used to come to the conclusion is sound but it wouldn't be the first time I made I mistake. Secondly, assuming the list is accurate, is it intended and/or expected behaviour or is it a bug? |
I tested a few different things your sample above and had some strange scoping issues too. Very confusing! Non working debug session. It seems to lose / reset the count when It is entered. Discovery: Starting test discovery in 1 test containers.
Starting discovery in 1 files.
Discovery: Discovering tests in my.tests.ps1
Discovery: Found 1 tests in 9 ms
Discovery: Processing discovery result objects, to set root, parents, filters etc.
Filter: (foo) Block will be included in the run, because there were no include filters, and will let its children to determine whether or not it should run.
Filter: (foo.bar) Test will be included in the run, because there were no include filters so all tests are included unless they match exclude rule.
Discovery finished in 80ms.
Discovery: Test discovery finished.
Mock: Setting up default mock for Write-Host.
Mock: We are in a block, one time setup or similar. Returning mock table from test block.
Mock: Resolving command Write-Host.
Mock: Searching for command Write-Host in the caller scope.
Mock: Found the command Write-Host in the caller scope.
Mock: Mock does not have a hook yet, creating a new one.
Mock: Defined new hook with bootstrap function PesterMock_9676dab5-0b84-4525-a803-8b9d0824159b and aliases Write-Host, Microsoft.PowerShell.Utility\Write-Host.
Mock: Adding a new default behavior to Write-Host.
Mock: Mock for Write-Host was invoked from block Process, resolving call history and behaviors.
Mock: Getting all defined mock behaviors in this and parent scopes for command Write-Host.
Mock: Finding all behaviors in this block and parent blocks.
Mock: Found behaviors for 'Write-Host' in 'foo'.
Mock: Found 1 behaviors for 'Write-Host':
Body: { }
Filter: $null
Verifiable: True
Mock: We are in a block, one time setup or similar. Returning mock table from test block.
Mock: Finding a mock behavior.
Mock: { } is a default behavior and will be used for the mock call.
Mock: Executing mock behavior for mock Write-Host.
Mock: Behavior for Write-Host was executed.
Mock: Resolving command Write-Host.
Mock: Searching for command Write-Host in the caller scope.
Mock: Found the command Write-Host in the caller scope and it resolved to PesterMock_9676dab5-0b84-4525-a803-8b9d0824159b.
Mock: Running mock filter { $True } with context: Object = .
Mock: Mock filter passed.
Mock: Resolving command Write-Host.
Mock: Searching for command Write-Host in the caller scope.
Mock: Found the command Write-Host in the caller scope and it resolved to PesterMock_9676dab5-0b84-4525-a803-8b9d0824159b.
[-] foo.bar 16ms (13ms|3ms)
Expected Write-Host to be called 1 times exactly but was called 0 times
at Should -Invoke @__params__p, C:\Program Files\WindowsPowerShell\Modules\Pester\5.0.2\Pester.psm1:11655
Mock: Removing function PesterMock_9676dab5-0b84-4525-a803-8b9d0824159b and aliases Write-Host, Microsoft.PowerShell.Utility\Write-Host for Write-Host.
Tests completed in 158ms
Tests Passed: 0, Failed: 1, Skipped: 0 NotRun: 0 A "working" script
|
I think we are saying the same thing, though it still leaves the question of whether it is a bug or intended behaviour. I expanded the test case a little further and here, I think, things get even weirder: Describe 'foo' {
BeforeAll {
[int]$callCount = 1
Mock Write-Host -Verifiable {}
Write-Host ''
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'throws' {
Mock Write-Host -Verifiable { throw 'ERROR!' }
{Write-Host '' } | Should -Throw
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'changes counts' {
$callCount = 2
Write-Host 'How now'
Write-Host 'brown cow?'
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'is unaffected by changes in previous It{} blocks' {
Write-Host ''
Should -Invoke Write-Host -Times $callCount -Exactly
}
AfterAll {
Should -Invoke Write-Host -Times $callCount -Exactly
}
} Based on our previous tests I would expect this to work but, again, it does not:
The report explicitly states that the tests ( What I believe we can infer from this is that:
The first 4 points make sense to me but the last 2 seem counter-intuitive. And point 6.1 seems especially wrong, in my opinion. I am new to all of this, though, so there may be a good reason for it that I just am not aware of. EDIT: Update validations to use recommended syntax. Slightly reword list of inferred facts to hopefully make it clearer. |
I took a closer look at the v5 ReadMe and confirmed that this is expected behaviour. Here is a fixed version of the expanded example in my previous comment: Describe 'foo' {
BeforeAll {
[int]$callCount = 1
Mock Write-Host -Verifiable {}
Write-Host ''
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'counts the parent' {
Should -Invoke Write-Host -Times $callCount -Exactly -Scope Describe
}
It 'throws' {
Mock Write-Host -Verifiable { throw 'ERROR!' }
{ Write-Host '' } | Should -Throw
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'changes counts' {
$callCount = 2
Write-Host 'How now'
Write-Host 'brown cow?'
Should -Invoke Write-Host -Times $callCount -Exactly
}
It 'is unaffected by changes in previous It{} blocks' {
{ Write-Host '' } | Should -Not -Throw
Should -Invoke Write-Host -Times $callCount -Exactly
}
AfterAll {
# Shouldn't this be 4?
# 1 - BeforeAll
# 0 - It 'throws' (Mock is redefined)
# 2 - It 'changes counts'
# 1 - It 'is unaffected by changes in previous It{} blocks'
Should -Invoke Write-Host -Times 5 -Exactly
}
} I still don't understand why the call to the child Mock in |
1. General summary of the issue
It seems that Mocks called within a
BeginAll{}
block cannot be verified in an adjacent childIt{}
block.Based on what I have read it seems like it should work but maybe I am missing something.
Here is sample code to illustrate what I mean:
2. Describe Your Environment
Pester version : 5.0.2 C:\Program Files\WindowsPowerShell\Modules\Pester\5.0.2\Pester.psd1
PowerShell version : 5.1.18362.752
OS version : Microsoft Windows NT 10.0.18363.0
3. Expected Behavior
The sample code should pass its test. Smallest sample that demonstrates the issue:
4.Current Behavior
The sample code returns the following error:
5. Possible Solution
6. Context
I am new to Pester and very new to v5. I ran across this while refactoring some tests that worked fine in v3.
I suspect that it is something that I just don't understand rather than an actual bug but I figured I would raise it since I can't find anything that seems to explain why it should work this way.
The text was updated successfully, but these errors were encountered: