Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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"
    }
}
like image 876
BenoitM Avatar asked Feb 28 '17 16:02

BenoitM


People also ask

How do you mock a function in Pester?

If you wish to mock commands that are called from inside a script module, you can do so by using the -ModuleName parameter to the Mock command. This injects the mock into the specified module. If you do not specify a module name, the mock will be created in the same scope as the test script.

What is a class mock?

A mock is a fake class that can be examined after the test is finished for its interactions with the class under test. For example, you can ask it whether a method was called or how many times it was called.

Should I be in Pester?

Should is a keyword that is used to define an assertion inside an It block. Should provides assertion methods to verify assertions e.g. comparing objects. If assertion is not met the test fails and an exception is thrown.


1 Answers

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.

like image 134
alx9r Avatar answered Oct 04 '22 01:10

alx9r