Skip to content
5 min read

Your First Test: Describe, It, and Should

  • PowerShell
  • Pester
  • Describe
  • It
  • Should
  • Beginners

The smallest useful Pester test is three words and one assertion. If you can write a sentence, you can write a test. In this post you'll write your first one, run it, and then deliberately break the code to watch the test catch you, which is the whole point. Make sure you have Pester 5.x loaded first (see Part 2).

What you'll learn

  • The three building blocks: Describe, It, and Should
  • How to write and read a one-assertion test
  • How to test a tiny real function
  • How to make a test fail on purpose (red) and then pass (green)

The anatomy of a test

Every Pester test is built from three nested ideas:

  • Describe groups related tests. Think of it as the subject: "the thing I am testing."
  • It states one single behavior in plain language: "it adds two numbers."
  • Should is the actual claim, the assertion that either holds or does not.

Read together they form a sentence: Describe Add, It adds two numbers, the result Should -Be 4. Here is that exact test:

function Add { param([int]$A, [int]$B) $A + $B }

Describe 'Add' {
    It 'adds two numbers' {
        Add 2 2 | Should -Be 4
    }
}

Save that as Add.Tests.ps1 and run it with Invoke-Pester ./Add.Tests.ps1. You will see one passing test. (We dig into running and reading output in Part 4; for now, just notice the green.)

How Should works

Should is a command you pipe a value into. The shape is always:

$actualValue | Should -Operator $expectedValue

The pipe matters. You pipe the actual result on the left into Should, and -Be (or another operator) compares it to the expected value on the right. In our example, Add 2 2 produces 4, that 4 flows into Should, and -Be 4 confirms it. If the values differ, the test fails and tells you both numbers.

Testing a tiny real function

1 + 1 is a fine warm-up, but let us test something that looks like real code: a Get-Square function.

function Get-Square {
    param([int]$Number)
    $Number * $Number
}

Describe 'Get-Square' {
    It 'returns 25 for an input of 5' {
        Get-Square -Number 5 | Should -Be 25
    }

    It 'returns 0 for an input of 0' {
        Get-Square -Number 0 | Should -Be 0
    }
}

Two behaviors, two It blocks, each with one clear claim. Notice the It names: they describe the behavior and the expected result, not "works" or "test1." A good name reads like a specification line.

Red, then green

The most important habit in testing is watching a test fail for the right reason. A test that has never failed might be testing nothing at all. So let us break the function on purpose.

Change the multiplication to addition, an easy mistake a tired human might make:

function Get-Square {
    param([int]$Number)
    $Number + $Number   # BUG: should be *, not +
}

Describe 'Get-Square' {
    It 'returns 25 for an input of 5' {
        Get-Square -Number 5 | Should -Be 25
    }
}

Run it and the test goes red. Pester reports something like:

Expected 25, but got 10.

5 + 5 is 10, not 25, and the test caught it instantly, no eyeballing required. Now fix the function back to $Number * $Number, rerun, and watch it return to green. That red-to-green cycle is the heartbeat of testing: the failure proves the test is real, and the pass proves the fix worked.

Try it yourself

  1. Save Get-Square and its test in a file named Get-Square.Tests.ps1.
  2. Run it with Invoke-Pester ./Get-Square.Tests.ps1 and confirm it passes (green).
  3. Break the function (change * to +) and run again. Read the failure message carefully, note how it tells you exactly what it expected versus what it got.
  4. Fix it and confirm you are back to green.

Doing this once builds the instinct for life: a test you have never seen fail is a test you cannot trust.

Common mistakes

Putting logic outside It. Calculations, loops, or assertions placed directly in Describe (outside any It) behave unexpectedly in Pester 5 because of its two-phase run model. Keep your actual test work inside It blocks. (Part 10 explains the phases.)

Vague It names. "it works" or "test 1" tells you nothing when it fails at 2 a.m. Name the behavior and the expectation: It 'returns 25 for an input of 5'.

Forgetting to pipe into Should. Writing Should -Be 25 with no value piped in does not assert on your result. The actual value must flow in: Get-Square -Number 5 | Should -Be 25.

Recap

A Pester test is Describe (the group), It (one behavior), and Should (the claim), and the smallest useful one is a single piped assertion. You wrote a real Get-Square test, then broke the function to see the test turn red before fixing it green. That deliberate failure is not a detour, it is how you prove the test actually works. Keep your logic inside It and always pipe the actual value into Should.

Next up: Part 4 — Running Tests with Invoke-Pester and Reading the Output, where we slow down on that failure message and learn to read Expected-vs-But-was, run whole folders, and understand the exit codes that CI relies on.