-
I am using Repro example, the real BeforeAll {
function DownloadStuff {
$stuff = Invoke-RestMethod 'example.com/api/stuff' -SkipHttpErrorCheck -StatusCodeVariable scv
@( $scv, $stuff ) # Throws RuntimeException: The variable '$scv' cannot be retrieved because it has not been set.
}
}
Describe 'DownloadStuff' {
Context 'Forbidden' {
BeforeEach {
Mock Invoke-RestMethod {
Set-Variable -Scope 1 'scv' 401
'no data'
}
$result = DownloadStuff
}
It 'Returns 401' {
$result[0] | Should -Be 401
}
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 5 replies
-
This a great question! There's two versions of this answer: one for your sample and one for your production code. Describe 'DownloadStuff in same session state' {
BeforeAll {
function DownloadStuff {
# $demoVar = 123
$stuff = Invoke-RestMethod 'example.com/api/stuff' -SkipHttpErrorCheck -StatusCodeVariable scv
@( $scv, $stuff )
}
}
Context 'Forbidden' {
BeforeEach {
Mock Invoke-RestMethod {
# We need to find the correct Scope-level for DownloadStuff
# 1. Uncomment $demoVar in DownloadStuff
# 2. Set a breakpoint on next line and debug test
# 3. When it stops on the breakpoint, try Get-Variable demoVar -Scope 2 (then 3, 4 etc. until match)
Set-Variable -Scope 3 'scv' 401
'no data'
}
$result = DownloadStuff
}
It 'Returns 401' {
$result[0] | Should -Be 401
}
}
} It will not work as-is in your production code because The mock scriptblock is defined in the test code, which runs in the global (default) session state, while the code in We solve this by making the mock run in the module's session state as well. Describe 'DownloadStuff in module state' {
BeforeAll {
Get-Module DemoModule | Remove-Module
New-Module DemoModule {
function DownloadStuff {
# $demoVar = 234
$stuff = Invoke-RestMethod 'example.com/api/stuff' -SkipHttpErrorCheck -StatusCodeVariable scv
@( $scv, $stuff )
}
} | Import-Module
}
Context 'Using a bound scriptblock' {
BeforeEach {
# We need to bind the scriptblock to the module state so it has access to the same variable scopes as DownloadStuff
$moduleBoundScriptblock = (Get-Module DemoModule).NewBoundScriptBlock({
# Find the right Scope-value. This will be different from the previous example
Set-Variable -Scope 2 'scv' 401
'no data'
})
Mock Invoke-RestMethod -ModuleName DemoModule -MockWith $moduleBoundScriptblock
$result = DownloadStuff
}
It 'Returns 401' {
$result[0] | Should -Be 401
}
}
Context 'Creating mock with InModuleScope' {
BeforeEach {
InModuleScope DemoModule {
# Running inside the module will automatically bind the scriptblock to the module state
# + automatically publish mock in the module (no need for -ModuleName ..)
Mock Invoke-RestMethod {
# Find the right Scope-value. This will be different from the previous example
Set-Variable -Scope 3 'scv' 401
'no data'
}
}
$result = DownloadStuff
}
It 'Returns 401' {
$result[0] | Should -Be 401
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Thank you so much for the detailed writeup! Changing Maybe it would be worth adding such example to https://pester.dev/docs/usage/mocking ? There is nothing about |
Beta Was this translation helpful? Give feedback.
-
One more thing, not that crucial as I went with a different approach that does not need this, but still interesting. Any way to access variables from the current module when using approaches above? My motivating example is a helper method for creating mocks: BeforeAll {
function Mock-RestMethod($status, $result) {
InModuleScope TestModule {
Mock Invoke-RestMethod {
Set-Variable -Scope 3 'scv' $status
$result
}
}
}
}
It 'Returns 401' {
Mock-RestMethod 401 'no data'
$result = DownloadStuff
$result[0] | Should -Be 401
$result[1] | Should -Be 'no data'
} I found that I can pass variables into I have no issues with using helpers for mocking |
Beta Was this translation helpful? Give feedback.
-
I tried using closures per your blog in https://frodeflaten.com/posts/generating-pester-mocks/ , but no luck, maybe messed up the syntax somewhere, will try again tomorrow and post some code if still no dice. |
Beta Was this translation helpful? Give feedback.
This a great question! There's two versions of this answer: one for your sample and one for your production code.
Your sample should work when you find the right scope-level to use with
Set-Variable
. Example: