Author Posts

August 8, 2016 at 10:02 am

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 2 years ago by  Anthony.
  • This topic was modified 2 years ago by  Anthony.

August 8, 2016 at 9:16 pm

@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 

August 9, 2016 at 1:33 am

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!

August 9, 2016 at 4:52 pm

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.