Validate a list of files in PowerShell with Pester

152 Views Asked by At

I want to create a PowerShell script that can delete a bunch of file in a given paths following a particular match (in the following example all the files that contain TMP). This is the function:

function CheckFilesToDelete([string[]]$fList) {
    [System.Collections.Generic.List[System.IO.DirectoryInfo]]$fileList

    foreach($folderName in $fList)
    {
        $path = "$folderName"

        ($list = @(Get-ChildItem "$path" -File -Recurse | 
                 Where-Object { $_.Name -match '.*TMP.*' })) > null
        if($list.Count -gt 0) { $fileList.AddRange($list) }
    }

    return $fileList
}

Now, I want to create the test for this function. I mock the Get-ChildItem so I can verify what files the function has to return. I like to have all the details of the files (such as name, creation date and so on)

Describe "Validate files to delete" {
    Context "validate files" {
        It "should return a list of expected files" {
            [string[]]$folderList = "Tests"
            $tmpPath = Join-Path $path $_

            [System.Collections.Generic.List[System.IO.DirectoryInfo]]
            $expected = @('1_TMP.txt','2_TMP.txt')

            Mock Get-ChildItem {
                [PSCustomObject]@{ Name = '1_TMP.txt' },
                [PSCustomObject]@{ Name = 'SmokeTest.txt' },
                [PSCustomObject]@{ Name = '2_TMP.txt' },
                [PSCustomObject]@{ Name = 'qq02000.doc' }
            }

            $list = CheckFilesToDelete -EnvPath $path 
                    -fList $folderList -ErrorAction Stop
        }
    }
}

The problems I'm facing are:

  • how can I display the $list to check the list of files
  • how to compare $list with $expected
1

There are 1 best solutions below

0
Frode F. On

You can display $list by setting a breakpoint and debugging it, or adding Write-Host $list in the test temporary

You can compare using the assertion below which matches count and filenames. Sort first to avoid order-issues: $expected.Name | Sort-Object | Should -Be ($list.Name | Sort-Object)

Tip: For a simple filename-filter I'd recommend using $list = @(Get-ChildItem -Path "$path" -File -Recurse -Filter '*TMP*'). It will process the filter in the filesystem-provider which is a lot faster than returning every file and filter in PowerShell like Where-Object and Get-ChildItem -Include '*TMP*' would.

Example:

BeforeAll {
    function CheckFilesToDelete([string[]]$fList) {
        $fileList = [System.Collections.Generic.List[System.IO.FileInfo]]::new()

        foreach ($folderName in $fList) {
            $path = "$folderName"

            # Using -Filter for improved performance for filename filter
            # Convert to typed array so .AddRange() doesn't complain about object[] parameter
            $list = [System.IO.FileInfo[]]@(Get-ChildItem -Path $path -File -Recurse -Filter '*TMP*')
            if ($list.Count -gt 0) { $fileList.AddRange($list) }
        }

        return $fileList
    }
}

Describe 'Validate files to delete' {
    Context 'validate files' {
        It 'should return a list of expected files' {
            [string[]]$folderList = 'Tests'

            $expected = [System.Collections.Generic.List[System.IO.FileInfo]]::new()
            $expected.Add([System.IO.FileInfo]::new('1_TMP.txt'))
            $expected.Add([System.IO.FileInfo]::new('2_TMP.txt'))

            Mock Get-ChildItem {
                $arr = @(
                    [System.IO.FileInfo]::new('1_TMP.txt'),
                    [System.IO.FileInfo]::new('SmokeTest.txt'),
                    [System.IO.FileInfo]::new('2_TMP.txt'),
                    [System.IO.FileInfo]::new('qq02000.doc'))

                # Mocking the -Filter parameter if used
                if ($PesterBoundParameters.Filter) {
                    return $arr | Where-Object Name -Like $PesterBoundParameters.Filter
                }

                return $arr
            }

            $list = CheckFilesToDelete -EnvPath $path -fList $folderList -ErrorAction Stop

            # Asserting by name since we don't have consistent paths in mocked objects
            $list.Name | Sort-Object | Should -Be ($expected.Name | Sort-Object Name)
        }
    }
}

Originally answered here: https://github.com/pester/Pester/discussions/2344#discussioncomment-5783009