0

I am creating a module which contains a class file as well as a unit test file (Pester).

The class B in the class file inherits from a class A found in another module.

If I try to run a unit test that mocks class B by inheriting it, I get "Unable to find type [B]"

However, If I simply instantiate class B, everything works.

Why can't I inherit from the class, when I am able to instantiate it?

Unit test file:

using module ".\MyModule.psd1" 

BeforeAll {
    Import-Module $PSScriptRoot\MyModule.psd1 -Force
}

Describe 'My unit test 1' {
    It 'Runs unit test 1' {
        
            # Mock a private function
            # Gives: Unable to find type [B].
            class MockedClassB : B
            {
                MockedClassB ([int]$foo) : base($foo)
                {
                }

                [string] MyMethod() { return 'MockedString' }
            }

            $mockedB = [MockedClassB]::new(13)
            $mockedB.Run()   
    }
}


Describe 'My unit test 2' {
    It 'Runs unit test 2' {
        
        # No errors.
        $instanceB = [B]::new(13)
        $instanceB.Run()   
    }
}

MyModule.psd1:

@{
RootModule = 'Include.psm1'
ModuleVersion = '1.0.0'

# This is needed to use classes. Otherwise the Test file will give "Unable to find type"
ScriptsToProcess = @(
      'Classes\B.ps1'
   )


RequiredModules = ''

# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @() 

# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()

# Variables to export from this module
# VariablesToExport = '*'

# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()

}

Include.psm1:

#Requires -Version 5.0
[cmdletbinding()]
param()

Write-Verbose $PSScriptRoot
Write-Verbose 'Import Classes in order because of dependencies'
$classList = @(
    'B'
)

foreach($class in $classList)
{
    Write-Verbose " Class: $class"
    . "$psscriptroot\Classes\$class.ps1"
}

B.ps1:

using module ".\..\..\ModuleContainingClassA\ModuleContainingClassA.psd1"

class B: A
{
    # ctor
    B([int]$foo)
    {
    }
}

[EDIT] If I replace the dot-including of the classes in the module file Include.psm1, and instead just let it contain the class definition(s), the error disappears.

I also found a similar question here, without any accepted answer: Mocking class functions with Pester 5 and PowerShell 7

5
  • The using module statement needs to point directly to the script module containing the source definition for the class, change using module ".\MyModule.psd1" to using module ".\MyModule.psm1" Commented Nov 1, 2022 at 11:17
  • Are you sure? I have been using .psd1 before and it always worked. Commented Nov 1, 2022 at 11:38
  • Added more information... Commented Nov 25, 2022 at 13:21
  • I would recommend you dot sourcing the classes.ps1 file in your .psm1 instead of relying in ScriptsToProcess. Both work but the first works better imo Commented Nov 25, 2022 at 13:22
  • Neither works for me... Commented Nov 25, 2022 at 14:02

1 Answer 1

0

This is possible if the using module statement is embedded in a Here-String and invoked with InModuleScope -ScriptBlock like so:

# InModuleScope is needed when using classes to prevent 'Unable to find type'
InModuleScope MyModule {

  Describe 'My unit test 1' {
    BeforeAll {
        <# Mock a private method

         Must use a Here-String because we need to pass 'using' which must be first in a scriptblock, but if it is outside the here-string then
         PowerShell will fail to parse the test script with 'Unable to find type [NameOfClass]'.
         Also, the whole thing must be inside InModuleScope as usual when dealing with classes.
        #>
        $inModuleScopeScriptBlock = @'
        using module ".\MyModule.psd1"

        class MockedClassB : B
        {
            MockedClassB ([int]$foo) : base($foo)
            {
            }

            [string] MyMethod() { return 'MockedString' }
        }

        $script:mockedB = [MockedClassB]::new(13)
'@
        InModuleScope -ModuleName MyModule -ScriptBlock ([Scriptblock]::Create($inModuleScopeScriptBlock))
            
             
    }

    It 'Runs unit test 1' {
        $mockedB.Run()            
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.