Search code examples
classpowershellpester

Pester mock method for Powershell 5 class


I am having an issue trying to mock a powershell 5 class method, when executing the test, I get the error " CommandNotFoundException: Could not find Command FunctionToMock". I am trying to unit test the "OutputToOverwrite" method by mocking "FunctionToMock". I think I would have to mock ChocoClass itself first, but I am not sure how to do it. Thanks.

Class ChocoClass
{
    [string] OutputToOverwrite()
    {
        return $this.FunctionToMock()
    }

    [string] FunctionToMock()
    {
        return "This text will be replaced"
    }
}


Describe "Testing mocking"{
    it "Mock test"{
        Mock FunctionToMock -MockWith {return "mystring"}
        $package = New-Object ChocoClass
        $expected = $package.OutputToOverwrite()
        $expected | should BeExactly "mystring"
    }
}

Solution

  • I have seen two ways to do this:

    1. Separate the bulk of the implementation into a function.
    2. Inherit from the class and override the method.

    (1) Use a Function

    I have been separating the implementation of methods into functions like this:

    Class ChocoClass
    {
        [string] OutputToOverwrite()
        {
            return $this.FunctionToMock()
        }
    
        [string] FunctionToMock()
        {
            return FunctionToMock $this
        }
    }
    
    function FunctionToMock
    {
        param($Object)
        return "This text will be replaced"
    }
    

    With that change, your test passes on my computer. This avoids PowerShell-class-related pitfalls but also avoids testing class behavior.

    (2) Derive and Override the Method

    You can derive the class and override the method you want to mock:

    Describe "Testing mocking"{
        it "Mock test"{
            class Mock : ChocoClass {
                [string] FunctionToMock() { return "mystring" }
            }
            $package = New-Object Mock
            $expected = $package.OutputToOverwrite()
            $expected | should BeExactly "mystring"
        }
    }
    

    This test passes on my computer. I haven't used this method for production code yet, but I like how direct it is. Watch out for problems related to re-defining classes with the same name in a single PowerShell session (see side note below).


    Side note: The separation of (1) minimizes the amount I run into this bug that prevents classes from being reloaded when you make changes to them. I have found, though, that the better workaround is to invoke each test run in a new PowerShell session (e.g. PS C:\>powershell.exe -Command { Invoke-Pester }) so I'm leaning toward (2) now.