Comments (10)
@csandfeld Interesting find, but it's not related to this issue. I'm curious why they don't provide the same Ast though, so might ask on the PowerShell discord or github-repo 🤔
The root cause is that Pester assumes that all provided container scriptblocks are bound to the same session state used when calling Invoke-Pester
("caller state"). It injects the provided parameters (Data) in that session state only, so if the scriptblocks are executed in a different session state they won't see those variables.
Using [scriptblock]::Create
or {}.Ast.GetScriptblock()
creates an unbound scriptblock, meaning it doesn't have a session state. As a result it is mistakenly executed in Pester's internal module state, while the variables are set in the default "terminal/script/global" state.
If possible Pester should detect unbound container scriptblocks and set the caller state to avoid being executed in Pester's internal state.
Bonus issue:
The same issue would occur by either generating the scriptblocks OR calling Invoke-Pester
from a module as both cases would cause mismatching session states. So a follow up improvement might be to always set the variables in the container's session state.
from pester.
Interesting find @GitHubEddie. I had a look at the AST for each "type" of scriptblock, and I guess the problem is caused by missing data in the AST Parent
property - check this out.
There IS data in the Parent
if scriptblock is created using curly braces ...
{ 'Script block using curly braces' }.Ast
# Output
Attributes : {}
UsingStatements : {}
ParamBlock :
BeginBlock :
ProcessBlock :
EndBlock : 'Script block using curly braces'
DynamicParamBlock :
ScriptRequirements :
Extent : { 'Script block using curly braces' }
Parent : { 'Script block using curly braces' }
... while there is NO data in the Parent
property if scriptblock is created using the [scriptblock]
accelerator ...
[scriptblock]::Create('Script block using accelerator').Ast
# Output
Attributes : {}
UsingStatements : {}
ParamBlock :
BeginBlock :
ProcessBlock :
EndBlock : Script block using accelerator
DynamicParamBlock :
ScriptRequirements :
Extent : Script block using accelerator
Parent :
... and there is also NO data in the Parent
property if scriptblock is created using the full [System.Management.Automation.ScriptBlock]
class
[System.Management.Automation.ScriptBlock]::Create('Script block using full class name').Ast
# Output
Attributes : {}
UsingStatements : {}
ParamBlock :
BeginBlock :
ProcessBlock :
EndBlock : Script block using full class name
DynamicParamBlock :
ScriptRequirements :
Extent : Script block using full class name
Parent :
Verified this on Windows PowerShell 5.1.22621.2506
and PowerShell 7.4.0
from pester.
... or by the difference in the Extent
property for that matter
from pester.
Thanks for the detailed report.
@GitHubEddie: May I ask why you'd want to use [scriptblock]::Create(string script)
in the first place when you already have a scriptblock? It will convert your scriptblock to a string, then create a scriptblock from it - which is really redundant. Instead you get weird issues like this and broken debugging. Are you getting the real code as text from external data/system?
@nohwnd The unbound scriptblock is executed in Pester's session state. Anything we can fix without breaking other scenarios?
from pester.
@fflaten, thanks for the explanation, it has been enlightening.
May I ask why you'd want to use
[scriptblock]::Create(string script)
in the first place when you already have a scriptblock?
The example given was for brevity, this is what I am actually doing.
I have a series of data driven tests that relate to individual systems, Active Directory for example. I keep these tests along with their pester configuration settings in PowerShell data files (.psd1).
The data files end up looking something like this:
# PesterConfiguration.Demo.Tests.psd1
@{
TestResult = @{
Enabled = $true
OutputFormat = 'JUnitXml'
}
Output = @{
Verbosity = 'Detailed'
CIFormat = 'Auto'
}
Run = @{
Exit = $false
SkipRemainingOnFailure = 'None'
PassThru = $true
Container = @(
@{
Scriptblock = {
param($PARAM1)
BeforeDiscovery {
write-host ('BeforeDiscovery PARAM1: {0}' -f $PARAM1)
}
BeforeAll {
write-host ('BeforeAll PARAM1: {0}' -f $PARAM1)
}
Describe '<PARAM1> attempt' {
It '<PARAM1> attempt' {
$PARAM1 | Should -Be 'param1 test'
}
}
}
Data = @{
PARAM1 = 'param1 test'
}
}
)
}
}
I safely import the data file and add it to new-pestercontainer
and new-pesterconfiguration
objects. This is where the odd scriptblock behaviour begins.
If you run this for example, where the imported scriptblock is injected directly into the pester container:
$import = Import-PowerShellDataFile -Path 'C:\PesterConfiguration.Demo.Tests.psd1'
$importScriptblock = $import.Run.Container[0].Scriptblock
$importData = $import.Run.Container[0].Data
$container = New-PesterContainer -ScriptBlock $importScriptblock -Data $importData
$config = New-PesterConfiguration -Hashtable $import
$config.run.Container = $container
$Result = invoke-pester -configuration $config
The test does not appear in the result:
Pester v5.5.0
Starting discovery in 1 files.
Discovery found 0 tests in 42ms.
Running tests.
Tests completed in 39ms
Tests Passed: 0, Failed: 0, Skipped: 0 NotRun: 0
If we look at the script block in a working container we get:
PS C:\> $config.Run.Container[0].Value[0].item
param($PARAM1)
BeforeDiscovery {
write-host ('BeforeDiscovery PARAM1: {0}' -f $PARAM1)
}
BeforeAll {
write-host ('BeforeAll PARAM1: {0}' -f $PARAM1)
}
Describe '<PARAM1> attempt' {
It '<PARAM1> attempt' {
$PARAM1 | Should -Be 'param1 test'
}
}
However, the script block in the container where the test is not in the result we see the issue is 'extra' curly braces:
PS C:\> $config.Run.Container[0].Value[0].item
{
param($PARAM1)
BeforeDiscovery {
write-host ('BeforeDiscovery PARAM1: {0}' -f $PARAM1)
}
BeforeAll {
write-host ('BeforeAll PARAM1: {0}' -f $PARAM1)
}
Describe '<PARAM1> attempt' {
It '<PARAM1> attempt' {
$PARAM1 | Should -Be 'param1 test'
}
}
}
These 'extra' curly braces come from the Import-PowerShellDataFile
function.
To accommodate this, I convert the script block to a string, remove the 'extra' curly braces, then convert it back to a script block.
This is why I have been using the [scriptblock]::Create
static method.
When the script block is in the right sessionstate without the extra curly braces the test runs as expected:
Pester v5.5.0
Starting discovery in 1 files.
BeforeDiscovery PARAM1: param1 test
Discovery found 1 tests in 45ms.
Running tests.
BeforeAll PARAM1: param1 test
Describing param1 test attempt
[+] param1 test attempt 8ms (2ms|6ms)
Tests completed in 124ms
Tests Passed: 1, Failed: 0, Skipped: 0 NotRun: 0
from pester.
Related Issues (20)
- Issues calling a Mock when a Mandatory attribute is included on a parameter HOT 3
- Should -HaveParameter -DefaultValue does not work for boolean types or integer value of 0 (regression) HOT 2
- Pester module warning 'CN=Jakub Jareš, O=Jakub Jareš, L=Praha, C=CZ' HOT 15
- Pester CI Pipeline Should Halt on 'BeforeDiscovery' Failure HOT 3
- Failure in Discovery when using different parameters across files HOT 3
- Select-Object and New-MockObject - Discarded PSObject HOT 2
- Mock CommandNotFoundException: Could not find Command Invoke-Sqlcmd HOT 4
- Bring back 'InconclusiveCount' property (or alternative) on returned Pester.Run object HOT 19
- I’m not a developer I run a nonprofit and a Pester Power Shell is running HOT 2
- Pester stopped working in vscode HOT 1
- v5.5 $____Pester.CurrentTest not populated in AfterEach HOT 4
- Missing timestamp from test suite attributes in junit xml export HOT 1
- Pester 5.5 reports incorrect Skipped results for Pending and Inconclusive HOT 3
- BeOfType doesn't see types, loaded from a file with classes, but the "It" function - does HOT 3
- Refer to a script in a parent directory HOT 2
- Does somthing like @afterstep exist for Pester 4.6 with Invoke Gherkin HOT 1
- New-RandomTempRegistry: Test-Path : No more data is available HOT 2
- Provide a way to more easily modify a mocked parameter before comparing with a test value. HOT 3
- Error during handling of output-element within Write-NUnit3OutputElement HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from pester.