Pester tests for DSC resource

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?

@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 

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!

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.