Pester in practice

The following function sets a computer description by modifying values in the registry:

function Set-ComputerDescription { 
    [CmdletBinding()] 
    param ( 
        [Parameter(Mandatory = $true)] 
        [AllowEmptyString()] 
        [String]$Description 
    ) 
 
    $erroractionpreference = 'Stop' 
 
    try { 
        $path = 'HKLM:SystemCurrentControlSetServicesLanmanServerParameters' 
 
        if ((Get-Item $path).GetValue('srvcomment') -ne $Description) { 
            if ($Description) { 
                Set-ItemProperty $path -Name 'srvcomment' -Value $Description 
            } else { 
                Remove-ItemProperty $path -Name 'srvcomment' 
            } 
        } 
    } catch { 
        throw 
    } 
} 

When the function interacts with the registry, it does so using the following commands:

  • Get-Item
  • Set-ItemProperty
  • Remove-ItemProperty

Testing the actions undertaken by each of the previous commands is not the responsibility of a unit test for Set-ComputerDescription. Unit tests are limited to ensuring that each of the commands has the right parameters, and at the right time. Each of the commands used by the function will be mocked.

The function reacts to a combination of the value of the Description parameter and the current state of the value.

A set of context blocks is appropriate for this division of the test. The difference between the blocks is the response from Get-Item, and is therefore the implementation of the Mock:

Describe Set-ComputerDescription { 
BeforeAll { Mock Set-ItemProperty Mock Clear-ItemProperty Mock Remove-ItemProperty
}
}

The first context is used to describe what happens when the current description is blank. A Mock for Get-Item is created, which returns a blank result. Tests are added, describing the behavior of the command:

Describe Set-ComputerDescription {
BeforeAll {
Mock Set-ItemProperty
Mock Clear-ItemProperty
Mock Remove-ItemProperty
}

Context 'Description is not set' {
BeforeAll {
Mock Get-Item {
[PSCustomObject]@{} | Add-Member GetValue -MemberType ScriptMethod -Value { '' }
}
}

It 'When the description differs, sets a new value' { Set-ComputerDescription -Description 'New description'
Assert-MockCalled Set-ItemProperty -Scope It } It 'When the description matches, does nothing' { Set-ComputerDescription -Description ''
Assert-MockCalled Set-ItemProperty -Times 0 -Scope It Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It }
}
}

The previous tests may be enhanced to ensure that Remove-ItemProperty is not called when updating with a new value. Given that the code paths are mutually exclusive, it should not be possible to call both. Extending the test ensures that future logic changes do not inadvertently trigger both commands.

The following context tests the actions that should be taken if a description is set. The Mock for Get-Item is replaced with one that returns a value:

Describe Set-ComputerDescription {
BeforeAll {
Mock Set-ItemProperty
Mock Clear-ItemProperty
Mock Remove-ItemProperty
}

Context 'Description is set' {
BeforeAll {
Mock Get-Item {
[PSCustomObject]@{} | Add-Member GetValue -MemberType ScriptMethod -Value {
return 'Current description'
}
}
}

It 'When the description differs, sets a new value' {
Set-ComputerDescription -Description 'New description'

Assert-MockCalled Set-ItemProperty -Scope It
}

It 'When the description matches, does nothing' {
Set-ComputerDescription -Description 'Current description'

Assert-MockCalled Set-ItemProperty -Times 0 -Scope It
Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It
}

It 'When the description is empty, removes the value' {
Set-ComputerDescription -Description ''

Assert-MockCalled Remove-ItemProperty -Times 1 -Scope It
}
}
}

The preceding tests might be enhanced to verify that an error will trigger the catch statement. For example, if Set-ItemProperty were to throw a non-terminating error with ErrorActionPreference set to Stop, a non-terminating error would be raised as a terminating error. The terminating error can be tested, as follows:

Describe Set-ComputerDescription {
BeforeAll {
Mock Set-ItemProperty
Mock Clear-ItemProperty
Mock Remove-ItemProperty
}

Context 'Error handling' {
BeforeAll {
Mock Set-ItemProperty {
Write-Error -Message 'Non-terminating error'
}
}

It 'When Set-ItemProperty throws, raises a terminating error' {
{ Set-ComputerDescription -Description 'New description' } | Should Throw
}
}
}

The following snippet combines each of the sections described previously:

Describe Set-ComputerDescription {
BeforeAll {
Mock Set-ItemProperty
Mock Clear-ItemProperty
Mock Remove-ItemProperty
}

Context 'Description is not set' {
BeforeAll {
Mock Get-Item {
[PSCustomObject]@{} | Add-Member GetValue -MemberType ScriptMethod -Value { '' }
}
}

It 'When the description differs, sets a new value' {
Set-ComputerDescription -Description 'New description'
Assert-MockCalled Set-ItemProperty -Scope It
Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It
}

It 'When the description matches, does nothing' {
Set-ComputerDescription -Description ''
Assert-MockCalled Set-ItemProperty -Times 0 -Scope It
Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It
}
}

Context 'Description is set' {
BeforeAll {
Mock Get-Item {
[PSCustomObject]@{} | Add-Member GetValue -MemberType ScriptMethod -Value {
return 'Current description'
}
}
}

It 'When the description differs, sets a new value' {
Set-ComputerDescription -Description 'New description'
Assert-MockCalled Set-ItemProperty -Scope It
}

It 'When the description matches, does nothing' {
Set-ComputerDescription -Description 'Current description'
Assert-MockCalled Set-ItemProperty -Times 0 -Scope It
Assert-MockCalled Remove-ItemProperty -Times 0 -Scope It
}

It 'When the description is empty, removes the value' {
Set-ComputerDescription -Description ''
Assert-MockCalled Remove-ItemProperty -Times 1 -Scope It
}
}

Context 'Error handling' {
BeforeAll {
Mock Set-ItemProperty { Write-Error -Message 'Non-terminating error' }
}

It 'When Set-ItemProperty throws, raises a terminating error' {
{ Set-ComputerDescription -Description 'New description' } | Should Throw
}
}
}
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.220.160.216