Skip to content
5 min read

Saying "Not": Negating Assertions Cleanly

  • powershell
  • pester
  • testing
  • assertions
  • negation

Should -Not -Be reads like plain English, which is exactly why it bites beginners. Negation is easy to write and easy to get wrong, because a test that says "this is not the bad value" can pass for reasons you didn't intend. In this post you'll learn where -Not goes, why negatives are often weak, and how to turn a wobbly negative claim into a confident positive one.

What you'll learn

  • Where -Not goes in the Should syntax
  • Why "not null" proves almost nothing on its own
  • How to prefer a strong positive assertion over a weak negative
  • Negating throws, matches, and collection membership
  • The traps: double negatives and -Not -Throw hiding detail

Where -Not goes

-Not is a switch on Should, and it sits between Should and the operator. It simply inverts whatever the operator would have claimed.

Describe 'Negation basics' {
    It 'places -Not before the operator' {
        'hello' | Should -Not -Be 'goodbye'
        5       | Should -Not -BeGreaterThan 10
        @(1,2)  | Should -Not -Contain 3
    }
}

That's the whole mechanic: Should -Not -Be, Should -Not -Match, Should -Not -Contain, Should -Not -Throw. If you find yourself writing Should -BeNot or -Not after the operator, flip the order.

Why "not null" is a weak claim

The most common negative assertion is Should -Not -BeNullOrEmpty, and it's far weaker than it looks. Consider a function that's supposed to return a user's full name:

function Get-FullName {
    param($User)
    "$($User.First) $($User.Last)"
}

It 'returns something (weak)' {
    $result = Get-FullName -User ([PSCustomObject]@{ First = $null; Last = $null })
    $result | Should -Not -BeNullOrEmpty
}

That test passes—even though the function returned " ", a single space. "Not null or empty" is satisfied by literally any non-empty value: a space, the word "error", a stack trace. The assertion told you the function returned a thing, not the right thing. A green test here gives false confidence.

Prefer a positive assertion

The fix is to state what you actually expect. Put the weak negative and the strong positive side by side:

# Weak: passes for " ", "error", anything non-empty
$result | Should -Not -BeNullOrEmpty

# Strong: passes only for the value you actually want
$result | Should -Be 'Ada Lovelace'

The positive version pins the behavior down. If the function regresses to returning " ", the positive assertion goes red with a clear "Expected 'Ada Lovelace', but got ' '." Whenever you catch yourself writing a negative, ask: what would a correct result actually be? and assert that instead.

Sometimes you genuinely don't know the exact value—say, a generated GUID. Even then you can usually find a stronger positive than "not null," such as a format check:

It 'returns a well-formed id (stronger than not-null)' {
    $id = [guid]::NewGuid().ToString()
    $id | Should -Match '^[0-9a-f]{8}-[0-9a-f]{4}-'   # shape, not just presence
}

When negation is the right tool

Negatives aren't banned—some claims are inherently negative, and there -Not is exactly right.

It 'uses negation where it genuinely fits' {
    # A removed item should NOT be in the list anymore:
    $remaining = @('apple', 'cherry')
    $remaining | Should -Not -Contain 'banana'

    # A sanitized string should NOT contain a script tag:
    'Hello world' | Should -Not -Match '<script>'

    # A valid call should NOT throw:
    { 1 + 1 } | Should -Not -Throw
}

The difference: these negate a specific thing ("not banana," "no script tag"), so they fail loudly when that exact thing reappears. A negative claim is strong when it rules out one definite outcome, and weak when it rules out only "nothing at all."

Try it yourself

Take this weak negative test and rewrite it as a stronger positive claim.

function Get-Greeting {
    param([string]$Name)
    "Hello, $Name!"
}

Describe 'Get-Greeting' {
    # Weak: passes even if the greeting is wrong, as long as it's non-empty
    It 'returns something' {
        Get-Greeting -Name 'Ada' | Should -Not -BeNullOrEmpty
    }

    # Your turn: replace the above with a positive assertion.
    It 'greets the person by name' {
        Get-Greeting -Name 'Ada' | Should -Be 'Hello, Ada!'
    }
}

Break the function (drop the ! or the comma) and notice that only the positive test catches it. The negative one stays green through the bug.

Common mistakes

  • Negatives that pass for the wrong reason. Should -Not -Be 'error' passes for 'erro', '', $null, and a million other values. You proved one outcome is absent, not that the correct one is present. Prefer Should -Be <the right value>.
  • Double negatives. Should -Not -BeFalse is a confusing way to write Should -BeTrue, and -Not -Not style stacking is illegal and unreadable. Express the claim positively.
  • -Not -Throw swallowing detail. { Do-Thing } | Should -Not -Throw passing only tells you it didn't blow up. If Do-Thing returns a value worth checking, assert that value instead—a passing Should -Be already implies no exception, and it tells you what came back, not just that nothing exploded.

Recap

-Not slots between Should and the operator and simply inverts the claim. Use it for genuinely negative facts—"not in the list," "no script tag," "did not throw on valid input"—but resist leaning on Should -Not -BeNullOrEmpty, which passes for any non-empty junk. The strongest test states the right answer with a positive assertion; reach for negation only when the claim is inherently about ruling one specific thing out.

Next up: Part 9 — Describe vs Context vs It: Organizing Intent, where we move from single assertions to structuring a growing test file so it reads like a specification.