Pester mock method for Powershell 5 class
Asked Answered
L

1

7

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"
    }
}
Lance answered 28/2, 2017 at 16:10 Comment(0)
R
6

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.

Recap answered 1/3, 2017 at 0:12 Comment(5)
Yes I thought about that solution, which works, but I would have to create a lot of functions to tests my class methods. Oh thanks for the side note, I thought I was doing something wrong or going crazy.Lance
I've heard from (or was warned by) more than one PowerShell MVP that PowerShell classes were created to support class-based DSC resources. Using PowerShell classes otherwise means you will encounter rough edges because that use hasn't been a priority for the PowerShell team to support. I'm using PowerShell classes for OOP, but in my first such project I had to write about 10x as much test code than production code around classes to figure out how things behaved.Recap
Many of those tests I published here. Look for the test files mentioning "class", "using", and "IEnumerable". I'll add a stackoverflow.com filter for PowerShell class questions so I see yours if you run into more rough edges.Recap
@Lance I added another way to do this that I think is closer to what you were hoping for.Recap
I am testing the usage of classes to simplify reusability of functions/library in scripts and manage the scope. I don't think I will need much of OOP. I really like solution(2). I will try in a complex class to see if it adds to much bloat in the test script, but it looks like it shouldn't.Lance

© 2022 - 2024 — McMap. All rights reserved.