Is there any reason why class defined in a module can't be subsclassed in Pester testcase? #1920
-
I recently discovered that attempting to subclass a class defined in the module under test results in an error, even though the class type is available. Consider this: SourceControl is a class defined in the module being tested class SourceControl {
hidden [PSCustomObject[]]$_allTags;
hidden [DateTime]$_headDate;
...
} In a Pester test case, with test code running InModuleScope BeforeAll {
Get-Module Elizium.Loopz | Remove-Module
Import-Module .\Output\Elizium.Loopz\Elizium.Loopz.psm1 `
-ErrorAction 'stop' -DisableNameChecking;
InModuleScope -ModuleName Elizium.Loopz {
# This class reference is ok:
#
[SourceControl]::New();
# Attempting to inherit from a class is not ok
#
class TestGit : SourceControl {
}
}
} results in error:
Currently, the only way around this is to define TestGit inside the module and reference the class from the test. However, this is sub-optimal because the module now contains test code. So what is the limitation here? Is this a PowerShell problem or a Pester problem, or am I doing something wrong? We clearly can't fix this via a using module statement at the top of the test file, because the module is the one being tested and is imported inside BeforeAll, which doesnt include class definitions, but the class is 'semi-visible'; we can new it, but not inherit from it. I'm just wondering what's the difference between these? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 7 replies
-
This is a PowerShell-issue. Repro: Get-Module demoModule | Remove-Module
$m = New-Module -Name demoModule -ScriptBlock {
class BaseDemo {
[string] echo ($message) {
return $message
}
}
} | Import-Module -PassThru
& ($m) {
# This class reference is ok:
#
$d = [BaseDemo]::New();
Write-Host $d.echo("hello world!")
# Attempting to inherit from a class is not ok
#
class TestGit : BaseDemo {
[string] SomeEcho ($message) {
return "hello {0}" -f $message
}
}
$d = [TestGit]::new()
Write-Host $d.SomeEcho("fflaten")
}
# Output
At line:9 char:21
+ class TestGit : BaseDemo {
+ ~~~~~~~~
Unable to find type [BaseDemo].
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : TypeNotFound It looks like the "class new : base" expression might be evaluated before the scriptblock is executed in the module session state where the base is available. If we create it as a string, it's delayed enough to work. Ex: Get-Module demoModule | Remove-Module
$m = New-Module -Name demoModule -ScriptBlock {
class BaseDemo {
[string] echo ($message) {
return $message
}
}
} | Import-Module -PassThru
& ($m) {
# This class reference is ok:
#
$d = [BaseDemo]::New();
Write-Host $d.echo("hello world!")
# Inherit from a module-defined class is ok
#
. ([ScriptBlock]::Create('
class TestGit : BaseDemo {
[string] SomeEcho ($message) {
return "hello {0}" -f $message
}
}'))
$d = [TestGit]::new()
Write-Host $d.SomeEcho("fflaten")
}
# Output
hello world!
hello fflaten |
Beta Was this translation helpful? Give feedback.
-
@fflaten, thanks for your response. You have confirmed what I feared and I'll just have to accept including a test class in my module. I tried you solution and although referencing the class from inside of the same BeforeAll block does actually work: BeforeAll {
Get-Module Elizium.Loopz | Remove-Module
Import-Module .\Output\Elizium.Loopz\Elizium.Loopz.psm1 `
-ErrorAction 'stop' -DisableNameChecking;
InModuleScope -ModuleName Elizium.Loopz {
. ([ScriptBlock]::Create('
class TestGit : SourceControl {
[string] SomeEcho ($message) {
return "hello {0}" -f $message
}
}'));
[TestGit]::new(); # => this class reference works ok
}
} ... accessing from inside a test, still doesnt work: It 'should: be able to reference class definition' {
InModuleScope Elizium.Loopz {
# Fails: RuntimeException: Unable to find type [TestGit]
[TestGit]::new();
}
} This is a bit of a moot point, because even if it did work, writing test code inside a non parsable string looks very precarious and probably inferior to just including the test code in the module. I'm just willing to accept this as one of the many things that are wrong/deficient with the current design of classes in PowerShell. |
Beta Was this translation helpful? Give feedback.
@fflaten, thanks for your response. You have confirmed what I feared and I'll just have to accept including a test class in my module. I tried you solution and although referencing the class from inside of the same BeforeAll block does actually work: