Pester tests for DSC resource

This topic contains 3 replies, has 2 voices, and was last updated by Profile photo of Steven Murawski Steven Murawski 4 months ago.

  • Author
    Posts
  • #49535
    Profile photo of Anthony
    Anthony
    Participant

    Hi,

    I'm trying to write some pester tests for one of my resources... I'm still relatively new to Pester but I've done a few before and the mocks have worked without any issues but this time it's doing my head in...

    The entire resource is here: https://github.com/theonlyway/xDSCSEPVIE/blob/dev/DSCResources/xDSCSEPVIE/xDSCSEPVIE.psm1
    The entire test are here: https://github.com/theonlyway/xDSCSEPVIE/blob/dev/Tests/xDSCSEPVIE.Tests.ps1

    What I appear to be having issues with is I am mocking the following commands, import-csv, get-volume and test-path. When I do some specific tests against those particular commands I can be sure they are being mocked correctly as per the global variables set.

    InModuleScope -ModuleName xDSCSEPVIE -ScriptBlock {
      $VIELocation = 'C:\Temp\doesntmatter.exe'
    
      $global:mockedVolume = [pscustomobject] @{
        FileSystemLabel = 'myLabel'
        DriveLetter     = 'C'
      }
      $global:mockedCSVSuccess = [pscustomobject] @{
        DriveLetter = 'C'
        DateScanned = (Get-Date -Format dd/MM/yyyy)
      }
      $global:mockedCSVFailure = @()
      $global:mockedCSVFailure += [pscustomobject] @{
        DriveLetter = 'C'
        DateScanned = (Get-Date -Format dd/MM/yyyy)
      }
      $global:mockedCSVFailure += [pscustomobject] @{
        DriveLetter = 'D'
        DateScanned = (Get-Date -Format dd/MM/yyyy)
      }
    
      Describe -Name 'Testing mocks' -Fixture {
        Mock -CommandName Import-CSV -MockWith {
          $global:mockedCSVSuccess
        }
        Mock -CommandName Get-Volume -MockWith {
          $global:mockedVolume
        }
        Mock -CommandName Test-Path -MockWith {
          return $true
        }
        It -name 'import-csv' -test {
          (Import-Csv).driveletter | Should Be 'C'
        }
        It -name 'get-volume' -test {
          (Get-Volume).driveletter  | Should Be 'C'
        }
        It -name 'test-path' -test {
          Test-Path -Path C:\windows\temp\VIEDrives.csv  | Should Be 'true'
        }
      }

    However when I get to this block

      Describe -Name "Testing $($Global:DSCResourceName)\Get-TargetResource present/absent logic" -Fixture {
        Mock -CommandName Import-CSV -MockWith {
          $global:mockedCSVSuccess
        }
        Mock -CommandName Get-Volume -MockWith {
          $global:mockedVolume
        }
        Mock -CommandName Test-Path -MockWith {
          return $true
        }
        It -name 'Get-TargetResource should return present' -test {
          (Get-TargetResource -VIELocation $VIELocation).Values | Should Be 'Present'
        }
        Mock -CommandName Import-CSV -MockWith {
          $global:mockedCSVFailure
        }
        It -name 'Get-TargetResource should return absent' -test {
          (Get-TargetResource -VIELocation $VIELocation).Values | Should Be 'Absent'
        }
      }

    It uses this function

    function Get-TargetResource
    {
      [CmdletBinding()]
      [OutputType([System.Collections.Hashtable])]
      param
      (
        [Parameter(Mandatory = $true)]
        [System.String]
        $VIELocation
      )
    
      $volumes = Get-Volume |
      Where-Object -FilterScript {
        $_.DriveType -eq 'Fixed' -and $_.FileSystemLabel -ne 'System Reserved' -and $_.DriveLetter -ne $null
      } |
      Sort-Object -Property DriveLetter
      if (Test-Path -LiteralPath 'C:\Windows\Temp\VIEdrives.csv') 
      {
        Write-Verbose -Message 'CSV exists'
        $csv = Import-Csv -Path 'C:\Windows\Temp\VIEdrives.csv'
        foreach ($drive in $csv) 
        {
          if ($drive.driveletter -in $volumes.driveletter) 
          {
            Write-Verbose -Message "Drive letter ($($drive.driveletter)) exists"
            return @{
              Ensure = 'Present'
            }
          }
          else 
          {
            Write-Verbose -Message "Drive letter ($($drive.driveletter)) does not exist"
            return @{
              Ensure = 'Absent'
            }
          }
        }
      }
      else 
      {
        Write-Verbose -Message 'CSV does not exists'
        return @{
          Ensure = 'Absent'
        }
      }
    }

    I get the following results

    Describing Testing mocks
     [+] import-csv 25ms
     [+] get-volume 8ms
     [+] test-path 7ms
    Describing Testing if functions return correct objects
    VERBOSE: CSV exists
    VERBOSE: Drive letter (C) does not exist
     [+] Get-TargetResource returns a hashtable 33ms
     [+] Test-TargetResource returns true or false 44ms
    Describing Testing xDSCSEPVIE\Get-TargetResource present/absent logic
    VERBOSE: CSV exists
    VERBOSE: Drive letter (C) does not exist
     [-] Get-TargetResource should return present 40ms
       Expected string length 7 but was 6. Strings differ at index 0.
       Expected: {Present}
       But was:  {Absent}
       -----------^
       at line: 77 in C:\Users\Anthony\Documents\GitHub\xDSCSEPVIE\Tests\xDSCSEPVIE.Tests.ps1
       77:       (Get-TargetResource -VIELocation $VIELocation).Values | Should Be 'Present'
    VERBOSE: CSV exists
    VERBOSE: Drive letter (C) does not exist

    I'm pretty sure the logic in the resource is sound because when I run it manually as a function it works properly as I would expect but the pester tests which I believe I am mocking correctly aren't producing the same results as when I run it manually and I can't figure out why.

    Anyone have any ideas?

    • This topic was modified 4 months ago by Profile photo of Anthony Anthony.
    • This topic was modified 4 months ago by Profile photo of Anthony Anthony.
  • #49648
    Profile photo of Steven Murawski
    Steven Murawski
    Participant

    @anthony – It looks like your mock of

     $global:mockedVolume = [pscustomobject] @{
        FileSystemLabel = 'myLabel'
        DriveLetter     = 'C'
      }
    

    doesn't have all the right properties to satisfy the below filter, which means you'll get $null for volumes and absent for c:\...

    $volumes = Get-Volume |
      Where-Object -FilterScript {
        $_.DriveType -eq 'Fixed' -and $_.FileSystemLabel -ne 'System Reserved' -and $_.DriveLetter -ne $null
      } |
      Sort-Object -Property DriveLetter 
    
    • #49666
      Profile photo of Anthony
      Anthony
      Participant

      Ah I was under the impression that when you mocked a command it would just intercept the command and just return what you wanted regardless of any filters you specified.

      But when you think about it logically that would probably be a really bad idea... Either way I'll remember that for the future.

      Thanks Steven, all working!

    • #49747
      Profile photo of Steven Murawski
      Steven Murawski
      Participant

      No problem! The mock does mock any filters provided to the command itself, but in this case you are piping the mocked output to other commands.

      This is a great place to extract a function – you could make a function in your module called something like Get-FixedReservedDisk
      and have

      function Get-FixedReservedDisk {
       Get-Volume |
        Where-Object -FilterScript {
          $_.DriveType -eq 'Fixed' -and $_.FileSystemLabel -ne 'System Reserved' -and $_.DriveLetter -ne $null
        } |
        Sort-Object -Property DriveLetter
      }
      

      Then when testing the Get-TargetResource, you could mock that function (rather than get-volume). You'd also want separate tests around Get-FixedReservedDisk.

You must be logged in to reply to this topic.