2

We're struggling to have Pester tests fail or pass depending on the equality of objects within an array.

Test.ps1

#require Assert
#require Pester

$Expected = @(
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
)
$Actual = @(
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
)

Describe 'comparing arrays' {
    Context 'Assert-Equivalent' {
        it 'should be green' {           
            Assert-Equivalent -Actual $Expected -Expected $Expected
        }
        it 'should be green' {        
            Assert-Equivalent -Actual $Actual -Expected $Expected
        }
        it 'should be red' {        
            $Wrong = @(
                [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
                [PSCustomObject]@{Name1 = 'WROMG';Name2 = 'Text2'}
            )
            Assert-Equivalent -Actual $Wrong -Expected $Expected
        }
    }
    Context 'Should be' {
        it 'should be green' {
            $Expected | Should -Be $Expected
        }
        it 'should be green' {
            $Actual | Should -Be $Expected
        }
        it 'should be red' {        
            $Wrong = @(
                [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
                [PSCustomObject]@{Name1 = 'WROMG';Name2 = 'Text2'}
            )
            $Wrong | Should -Be $Expected
        }
    }
}

We can't seem to get this right. Are we using the wrong CmdLets? Or is there another way of checking this? Sometimes the array is also just a property of another object. So an in depth compare would be needed.

DarkLite1
  • 13,637
  • 40
  • 117
  • 214

4 Answers4

4

When I want to compare some 'complex' objects with Pester, I use ConvertTo-Json. It's not ideal as there can be some false negative if types mismatch but it often does the job.

$Expected = @(
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
)
$Actual = @(
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
    [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
)

Describe 'comparing arrays' {
    Context 'Assert-Equivalent' {
        it 'should be green' {           
            Assert-Equivalent -Actual ($Expected | ConvertTo-Json)  -Expected ($Expected | ConvertTo-Json) 
        }
        it 'should be green' {        
            Assert-Equivalent -Actual ($Actual | ConvertTo-Json)  -Expected ($Expected | ConvertTo-Json) 
        }
        it 'should be red' {        
            $Wrong = @(
                [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
                [PSCustomObject]@{Name1 = 'WROMG';Name2 = 'Text2'}
            )
            Assert-Equivalent -Actual ($Wrong | ConvertTo-Json)  -Expected ($Expected | ConvertTo-Json) 
        }
    }
    Context 'Should be' {
        it 'should be green' {
            ($Expected | ConvertTo-Json) | Should -Be ($Expected | ConvertTo-Json)
        }
        it 'should be green' {
            ($Actual | ConvertTo-Json) | Should -Be ($Expected | ConvertTo-Json)
        }
        it 'should be red' {        
            $Wrong = @(
                [PSCustomObject]@{Name1 = 'Text1';Name2 = 'Text2'}
                [PSCustomObject]@{Name1 = 'WROMG';Name2 = 'Text2'}
            )
            ($Wrong | ConvertTo-Json) | Should -Be ($Expected | ConvertTo-Json)
        }
    }
}
Julien Nury
  • 297
  • 2
  • 14
  • This seems like a good workaround. It's strange that the object type matters when it's converted to JSON. Still looking into this, because it would be even cooler if it didn't look at the type of object at all. – DarkLite1 Jun 15 '18 at 10:39
2

It al depends how strict you want to be in this question, e.g.:

  • Is the order of the objects in the array important?
  • Do you want to typecast properties?

Anyways, I wrote a small helper for this:

Function Should-BeObject {
    Param (
        [Parameter(Position=0)][Object[]]$b, [Parameter(ValueFromPipeLine = $True)][Object[]]$a
    )
    $Property = ($a | Select-Object -First 1).PSObject.Properties | Select-Object -Expand Name
    $Difference = Compare-Object $b $a -Property $Property
    Try {"$($Difference | Select-Object -First 1)" | Should -BeNull} Catch {$PSCmdlet.WriteError($_)}
}

You can invoke it like this:

,$Actual | Should-BeObject $Expected

(Mind the comma in front of ,$Actual)

iRon
  • 20,463
  • 10
  • 53
  • 79
0

I didn't give it a try myself, but there is an Assert-Equivalent cmdlet from the https://github.com/nohwnd/Assert library that looks promising.

Grzegorz Smulko
  • 2,525
  • 1
  • 29
  • 42
0

Although this is an old question, I thought of showing another way to do it. The use of Compare-Object could come in handy when comparing complex objects.


$Expected = @(
    [PSCustomObject]@{Name1 = 'Text1'; Name2 = 'Text2' }
    [PSCustomObject]@{Name1 = 'Text1'; Name2 = 'Text2' }
)
$Actual = @(
    [PSCustomObject]@{Name1 = 'Text1'; Name2 = 'Text2' }
    [PSCustomObject]@{Name1 = 'Text1'; Name2 = 'Text2' }
)

Describe 'object equivalence' {
    It 'eq' {
        $eqs = $Actual | Compare-Object -ReferenceObject $Expected -Property @('Name1', 'Name2') -IncludeEqual | Select-Object -ExpandProperty SideIndicator
        $eqs | ForEach-Object { $_ | Should -Be '==' }
    }
}

Note that you need to specify all properties to be compared. Have a look more on that here.

Vivere
  • 1,919
  • 4
  • 16
  • 35