Skip to content
5 min read

Comparing Numbers, Strings, and Collections

  • powershell
  • pester
  • testing
  • assertions
  • collections

Equality is rarely the whole story. Real assertions sound like "the result is greater than zero," "the name matches a pattern," or "the list contains this user." In this post you'll pick up the operators that express those claims, plus how to assert against a single property of an object. These are the workhorses you reach for when -Be won't cut it.

What you'll learn

  • Numeric comparisons: -BeGreaterThan and -BeLessOrEqual (and their siblings)
  • String matching: -Match (regex) versus -Like (wildcards)
  • Collection checks: -Contain, -BeIn, and -HaveCount
  • How to assert on a single property of a [PSCustomObject]
  • Why the direction of -Contain and -BeIn matters

Numeric comparisons

When you care about a range rather than an exact value, use the comparison operators. They read just like their PowerShell -gt/-le cousins, but they live inside Should so you get a proper failure message.

Describe 'Numeric comparisons' {
    It 'compares magnitudes' {
        10 | Should -BeGreaterThan 5
        10 | Should -BeGreaterOrEqual 10
         3 | Should -BeLessThan 5
         3 | Should -BeLessOrEqual 3
    }
}

These are perfect for things like "the response time is under 200 ms" or "the retry count never exceeds three." They're also the safe way to compare floating-point results: instead of an exact -Be, assert the value falls within a tolerance.

It 'handles float precision safely' {
    $result = 0.1 + 0.2          # 0.30000000000000004 under the hood
    $result | Should -BeGreaterThan 0.299
    $result | Should -BeLessThan 0.301
}

String matching: -Match vs -Like

Two operators test string patterns, and beginners mix them up constantly because they use different pattern languages.

-Match uses regular expressions. The pattern is searched anywhere in the string (it's a partial match unless you anchor it):

It 'matches with a regular expression' {
    'ERROR: disk full' | Should -Match 'disk'
    'user-42'          | Should -Match '^user-\d+$'
}

-Like uses wildcards (* for any run of characters, ? for one), the same syntax you use with Get-ChildItem:

It 'matches with a wildcard' {
    'report-2026.csv' | Should -Like '*.csv'
    'report-2026.csv' | Should -Like 'report-????.csv'
}

Rule of thumb: reach for -Like for simple "starts with / ends with / contains" checks, and -Match when you need real pattern power. Just remember -Match treats its input as regex—see Common mistakes for the trap that causes.

Collections: -Contain, -BeIn, and -HaveCount

Here's the part everyone gets backwards at least once. -Contain and -BeIn ask the same question—"is this item in this collection?"—but the collection sits on opposite sides.

With -Contain, the collection is on the left and the item is on the right:

It 'checks membership with -Contain' {
    $fruits = @('apple', 'banana', 'cherry')
    $fruits | Should -Contain 'banana'      # collection -Contain item
}

With -BeIn, the item is on the left and the collection is on the right:

It 'checks membership with -BeIn' {
    'banana' | Should -BeIn @('apple', 'banana', 'cherry')   # item -BeIn collection
}

Read them as English and the direction sorts itself out: "the fruits contain banana" versus "banana is in the fruits." Pick whichever reads more naturally for the value you already have piped.

To assert how many items a collection holds, use -HaveCount:

It 'checks the size of a collection' {
    @('apple', 'banana', 'cherry') | Should -HaveCount 3
    @()                            | Should -HaveCount 0
}

-HaveCount is clearer and gives a better failure message than piping .Count into -Be, because it tells you the actual number of elements it found.

Asserting on an object's property

Most real functions return objects, not bare values. You assert on a property by reading it first, then piping that into Should:

Describe 'Object properties' {
    It 'asserts on individual properties' {
        $user = [PSCustomObject]@{
            Name    = 'Ada'
            Age     = 36
            Roles   = @('admin', 'editor')
        }

        $user.Name  | Should -Be 'Ada'
        $user.Age   | Should -BeGreaterThan 18
        $user.Roles | Should -Contain 'admin'
        $user.Roles | Should -HaveCount 2
    }
}

Each line is a small, independent claim about one property. If Age is wrong, that line fails and names the property, while the others keep passing—far easier to diagnose than one giant comparison.

Try it yourself

Given an array of user objects, assert the count and that a specific name is present.

Describe 'User list' {
    It 'has the expected users' {
        $users = @(
            [PSCustomObject]@{ Name = 'Ada';   Active = $true }
            [PSCustomObject]@{ Name = 'Linus'; Active = $true }
            [PSCustomObject]@{ Name = 'Grace'; Active = $false }
        )

        $users | Should -HaveCount 3
        $users.Name | Should -Contain 'Grace'
    }
}

Run it green, then add a fourth user and watch -HaveCount 3 go red. Change 'Grace' to a name nobody has and watch -Contain fail—note how the message lists what the collection did contain.

Common mistakes

  • Getting -Contain and -BeIn backwards. 'banana' | Should -Contain @(...) is wrong: -Contain expects the collection on the left. If the failure message looks like Pester is searching inside a single string, you've swapped the sides.
  • Forgetting -Match is regex. '192.168.0.1' | Should -Match '192.168.0.1' passes for the wrong reason, because . matches any character. To match a literal dot, escape it (192\.168\.0\.1) or use -Like with wildcards instead.
  • Comparing a collection with -Be. @(1,2,3) | Should -Be @(1,2,3) does not do an element-by-element array comparison the way you'd hope and can behave surprisingly. Use -HaveCount for size and -Contain/-BeIn for membership, or assert on the elements individually.

Recap

When equality isn't enough, -BeGreaterThan/-BeLessOrEqual (and friends) handle ranges and float tolerance, -Match brings regex while -Like brings wildcards, and -Contain/-BeIn/-HaveCount cover collection membership and size. Mind the direction of -Contain versus -BeIn, remember -Match is regex, and assert object properties one at a time for readable failures.

Next up: Part 7 — Testing That Things Fail: Should -Throw, where we stop testing the happy path and start proving your code rejects bad input the way it should.