简体   繁体   中英

How can Pester mock the “test” function in the pattern “test existence (not found) - create - test again to confirm creation”?

Here's some pseudo-code showing the function to test:

function Set-Something
{
    if (Test-Something)
    {
        return $True
    }

    # Not found so do something to create it.
    Do-Something

    # Check it's been created successfully.
    if (Test-Something)
    {
        return $True
    }

    return $False
}

This must be a fairly common pattern: "Test for existence - if not found create - test again to verify creation". Testing most branches is pretty simple but how can I test the branch where Test-Something fails the first time it's called, then succeeds the second time it's called?

This is my test code so far:

Describe 'Set-Something' {

    Context 'already exists' {
        Mock Test-Something { return $True }
        Mock Do-Something

        It 'returns True' {
            { Set-Something } | Should -Be $True
        }

        It 'does not call Do-Something' {
            Set-Something
            Assert-MockCalled Do-Something -Times 0 -Exactly
        }
    }

    Context 'does not already exist and creation fails' {
        Mock Test-Something { return $False }
        Mock Do-Something

        It 'calls Do-Something' {
            Set-Something
            Assert-MockCalled Do-Something -Times 1 -Exactly
        }

        It 'calls Test-Something twice' {
            Set-Something
            Assert-MockCalled Test-Something -Times 2 -Exactly
        }

        It 'returns False' {
            { Set-Something } | Should -Be $False
        }
    }

    Context 'does not already exist and creation succeeds' {
        Mock Test-Something { ?? }
        Mock Do-Something

        It 'calls Do-Something' {
            Set-Something
            Assert-MockCalled Do-Something -Times 1 -Exactly
        }

        It 'calls Test-Something twice' {
            Set-Something
            Assert-MockCalled Test-Something -Times 2 -Exactly
        }

        It 'returns True' {
            { Set-Something } | Should -Be $True
        }
    }
}

The case 'does not already exist and creation succeeds' is the problem. Test-Something needs to be mocked so it fails the first time it's called, and succeeds the second time. The arguments passed to Test-Something will be identical in each call so I can't use ParameterFilter to create two mocks of Test-Something with different behaviour.

I've found a couple of ways of mocking this:

1) Use a "static" (ie script-scoped) variable to record state

Context 'does not already exist and creation succeeds' {

    BeforeEach {
        $script:exists = $False
    }

    AfterAll {
        Remove-Variable exists -Scope Script
    }

    Mock Test-Something { 
        return $script:exists
    }

    Mock Do-Something {
        $script:exists = $True
    }

    It 'calls Do-Something' {
        Set-Something
        Assert-MockCalled Do-Something -Times 1 -Exactly
    }

    It 'calls Test-Something twice' {
        Set-Something
        Assert-MockCalled Test-Something -Times 2 -Exactly
    }

    It 'returns True' {
        { Set-Something } | Should -Be $True
    }
}

2) Use a hash table to record state

Context 'does not already exist and creation succeeds' {

    BeforeEach {
        $mockState = @{
                        ItExists = $False    
                    }
    }

    Mock Test-Something { 
        return $mockState.ItExists
    }

    Mock Do-Something {
        $mockState.ItExists = $True
    }

    It 'calls Do-Something' {
        Set-Something
        Assert-MockCalled Do-Something -Times 1 -Exactly
    }

    It 'calls Test-Something twice' {
        Set-Something
        Assert-MockCalled Test-Something -Times 2 -Exactly
    }

    It 'returns True' {
        { Set-Something } | Should -Be $True
    }
}   

Personally, I like the hash table, because $mockState. ... $mockState. ... seems to me to indicate the purpose of the variable better than $script:... . Also, a script-scoped variable could cause a race condition if the tests were ever parallelized and another Describe block modified the same variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM