Search code examples
unit-testingpowershellpester

Mocking a command and getting different results based on the number of times the Mock is called


I'm using Pester to unit test some code I've written. In the tests I mock Test-Path using a parameter filter:

Mock -CommandName 'Test-Path' -MockWith { return $false } `
    -ParameterFilter { $LiteralPath -and $LiteralPath -eq 'c:\dummy.txt' }

The following is psuedo code for what I'm doing:

If ( Test-Path -LiteralPath c:\dummy.txt )
{
    return "Exists"
}
else
{
    Attempt to get file

    If ( Test-Path -LiteralPath c:\dummy.txt )
    {
        return "File obtained"
    }   
}

On the first call to Test-Path I want to return $false and on the second call I want to return $true. I can think of a couple of ways of achieving this, but they don't feel right:

  1. On the first call use the Path parameter and on the second use LiteralPath. Have two mocks one with a ParameterFilter for each. I don't like the idea of hacking the code in order to facilitate a test.

  2. Create a function with parameters for: Path and InstanceNumber. Create mocks for the function. This is better than the above, but I don't like the idea of having a parameter just for testing purposes.

I've looked and can't find a way to mock based on the nth call. Does Pester facilitate this and I've just missed it? If not is there a cleaner way of achieving what I want?


Solution

  • function Test-File{
        If ( Test-Path -LiteralPath c:\dummy.txt )
                {
                    return "Exists"
                }
                else
                {
                    If ( Test-Path -LiteralPath c:\dummy.txt )
                    {
                        return "File obtained"
                    }   
                }
    }
    
    
    Describe "testing files" {
        it "file existence test" {
            #Arrange
            $script:mockCalled = 0
            $mockTestPath = {
                                $script:mockCalled++                                
                                if($script:mockCalled -eq 1)
                                {
                                    return $false
                                }
                                else
                                {
                                    return $true
                                }
                            }
    
                Mock -CommandName Test-Path -MockWith $mockTestPath 
                #Act
                $callResult = Test-File 
    
                #Assert
                $script:mockCalled | Should Be 2
                $callResult | Should Be "File obtained"
                Assert-MockCalled Test-Path -Times $script:mockCalled -ParameterFilter { $LiteralPath -and $LiteralPath -eq 'c:\dummy.txt' }
    
    
            }
    }
    

    I think you are after this?! let me know if not!