BeOfType doesn't see types, loaded from a file with classes, but the "It" function - does
exchange12rocks opened this issue · comments
Checklist
- Issue has a meaningful title
- I have searched the existing issues. See all issues
- I have tested using the latest version of Pester. See Installation and update guide.
What is the issue?
I have a .ps1 files containing definitions of my classes. I load that file in a BeforeAll
block. Later in my tests I can successfully create objects using these classes, but the Should-BeOfType
function does not see those types even so the It
function - does.
Running the test defined below, I receive the following output:
Starting discovery in 1 files.
Discovery found 1 tests in 880ms.
Running tests.
The type of TestO is: MyTestClass
[-] Test Classes.Object Creation.creates TestClass 717ms (616ms|102ms)
ArgumentException: Could not find type [MyTestClass]. Make sure that the assembly that contains that type is loaded.
Tests completed in 4.62s
Tests Passed: 0, Failed: 1, Skipped: 0 NotRun: 0
When executing the test step-by-step, I see that my class is indeed not available in the scope of the Should-BeOfType
function.
Expected Behavior
Types available in an It
block are available to use with the Should -BeOfType
command. The test defined below should pass successfully.
Steps To Reproduce
Describe 'Test Classes' {
BeforeAll {
. $PSScriptRoot\Classes.ps1
}
Describe 'Object Creation' {
It 'creates TestClass' {
$TestO = [MyTestClass]::new()
Write-Host ('The type of TestO is: {0}' -f $TestO.GetType().Name)
$TestO | Should -BeOfType [MyTestClass]
}
}
}
where Classes.ps1 is:
class MyTestClass {
}
Describe your environment
Pester version : 5.5.0 C:\Users\kf\Documents\PowerShell\Modules\Pester\5.5.0\Pester.psm1
PowerShell version : 7.4.0
OS version : Microsoft Windows NT 10.0.19045.0
Possible Solution?
No response
Thanks for the report.
TLDR: Using [MyTestClass]
as a parameter value or argument passes the value as a string. The type isn't resolvable by name internally in modules like Pester (see long version for why). If you wrap it in parentheses you'll pass the type-object itself which should work.
Try $TestO | Should -BeOfType ([MyTestClass])
or $TestO.GetType().Name | Should -Be 'MyTestClass'
Long version 🙂
By dot-sourcing Classes.ps1
you only define your classes locally in the current scope. Functions like Should
run in a different module-specific session state and scopes which can't see the class in your script scope. The issue is similar to running the script in your session without dot-sourcing.
# Run in child scope similar to your test file. Class is not available when the script is finished -> child scope with class imported is deleted
> & ./Classes.ps1; [MyTestClass]
InvalidOperation: Unable to find type [MyTestClass].
The typical solution would be to place the class in a module like Classes.psm1
and use e.g. using module .\Classes.psm1
at the top of your test-file to import it. That should've imported it in the global session state, making it available to every module and script. Unfortunately it doesn't. Maybe PowerShell imports the classes in the current scope in the global state which results in the same behavior.
Unfortunately working with classes in PowerShell has lots of weird behavior like this.
A few workarounds:
- Wrap the type in parentheses to pass the actual type-object
- Import your classes before running Pester using either
using module .\Classes.psm1
or dot-source. .\Classes.ps1
. That should make them available in all scopes and session states, including Pester's internal lookup that currently fails. - Use
$TestO.GetType().Name | Should -Be 'MyTestClass'
to assert the type name in this particular scenario.
Keep open until we've updated docs and function help with an example that passes the type-object properly (with parentheses).
Maybe also be clean up own internal tests, besides https://github.com/pester/Pester/blob/main/tst/functions/assertions/BeOfType.Tests.ps1 which is already good.
Thank you @fflaten for this explanation!