What Mocking Is and When You Actually Need It
- powershell
- pester
- testing
- mocking
- unit-testing
You don't want your unit test emailing a real customer, deleting a virtual machine, or hammering a production API. But your function does exactly those things, and you still need to know it works right. Mocking is the technique that lets you test the decision-making in your code while replacing the dangerous or slow parts with a harmless stand-in you control.
This post is about the idea of mocking. We won't write a real mock yet—that's Part 18. First we build the mental model so the mechanics click when they arrive.
What you'll learn
- What a mock actually is, in plain terms
- Why isolation makes a test fast, reliable, and safe
- Which commands are good candidates to mock
- When you should not mock something
- The difference between a mock, a stub, and a fake
A mock is a stand-in you control
Think of a film stunt double. The lead actor delivers the lines; the double takes the fall off the building. To the camera it looks like one person, but the risky part was handled by a substitute.
A mock is the same trick for code. You tell Pester, "for the duration of this test, when my function calls Send-MailMessage, do not really call it — run this harmless block instead." The real command is temporarily replaced. Your function still thinks it sent the email, so the logic that runs afterward behaves normally, but no message ever leaves the building.
The motivating example
Here is a function that decides whether to send an alert. Notice it mixes pure logic (the threshold check) with a real side effect (Send-MailMessage).
function Send-DiskAlert {
param(
[int]$FreePercent,
[string]$To
)
if ($FreePercent -lt 10) {
Send-MailMessage -To $To `
-From 'alerts@example.com' `
-Subject "Low disk: $FreePercent% free" `
-SmtpServer 'smtp.example.com'
return 'alert-sent'
}
return 'ok'
}What do you actually want to test? Two behaviors:
- When free space is below 10%, the function decides to alert.
- When it is at or above 10%, it does not.
What you do not want is for running the test to connect to an SMTP server and email someone. Without mocking, you cannot test behavior 1 at all without real consequences. With a mock of Send-MailMessage, you can verify the decision while the email goes nowhere.
Isolation is the whole point
A unit test checks one unit of logic in isolation. The moment your test depends on a mail server, a network share, or a live API, it stops being isolated. It becomes slow, flaky (the server might be down), and sometimes destructive.
Mocking restores isolation by cutting those external dependencies. The payoff:
- Safe — no email sent, no file deleted, no VM removed.
- Fast — no network round-trips or disk waits.
- Reliable — the test passes or fails based on your code, not on whether a remote service is healthy today.
- Deterministic — you decide exactly what the dependency returns, so you can test rare cases (a timeout, an empty result) on demand.
Good candidates to mock
Mock the commands that reach outside your function to touch the real world:
- Network calls —
Invoke-RestMethod,Invoke-WebRequest,Send-MailMessage. - Filesystem writes and deletes —
Set-Content,Remove-Item,New-Item. - Destructive operations —
Remove-VM,Stop-Service,Remove-ADUser. - External systems — database cmdlets, cloud SDK calls, REST wrappers.
- Slow or time-dependent calls —
Start-Sleep,Get-Date(when you need a fixed "now").
A quick test: if running this command for real would cost money, change a system, hit the network, or take seconds, it is a mocking candidate.
When NOT to mock
Mocking is a tool, not a default. Do not mock:
- Your own pure logic. If a function just transforms input to output with no side effects — string parsing, math, filtering — test it directly. There is nothing to isolate.
- The thing you are actually testing. If you mock the function under test, you are testing your mock, not your code.
- Cheap, deterministic, read-only built-ins where the real call is harmless and faster than the mock would be (for example,
Join-Path).
Over-mocking is its own bug, and we devote part of Part 21 to it. For now: mock the edges, test the middle.
Mock vs stub vs fake
These words get thrown around interchangeably. In plain language:
- Stub — a stand-in that just returns a canned value. "When asked, always return this."
- Mock — a stub that also records how it was called so you can later assert "it was invoked once, with these arguments."
- Fake — a lightweight working implementation, like an in-memory list pretending to be a database.
These distinctions aren't folk wisdom—they come from people who did the careful work of naming them. The umbrella term test double, and the catalog of stubs, mocks, fakes, dummies, and spies, is Gerard Meszaros's, from his book xUnit Test Patterns. And if the mock-versus-stub line finally clicks for you, thank Martin Fowler, whose essay Mocks Aren't Stubs made the difference legible for a whole generation of testers. I'm just passing their work along.
Pester's Mock command covers the first two: it returns what you tell it to (stub behavior) and tracks every call so you can verify interactions with Should -Invoke (mock behavior). You will rarely need a hand-built fake as a beginner.
Try it yourself
Open one of your own scripts. List every command it calls that you did not write — especially anything that hits the network, the filesystem, or another system. For each, write one line: "In a test, I would mock this so it returns ___ instead of really running." That list is your mocking to-do for when you start writing tests for that script.
Common mistakes
Mocking everything. If every dependency is faked, your test only proves the mocks were configured — not that your logic works. Mock the edges, keep the logic real.
Mocking pure functions. A function with no side effects has nothing to isolate. Mocking it adds noise and hides real behavior.
Not noticing a side effect exists. Beginners often miss that a "read" command also writes a cache file, or that a helper sends telemetry. Read what your dependencies actually do before deciding what to mock.
Recap
A mock is a controlled stand-in that replaces a real command during a test, giving you safety, speed, and isolation. Mock the commands that touch the outside world; leave your own pure logic alone. "Stub" returns a value, "mock" also records calls, and Pester's Mock does both.
Next up: Part 18 — Mock Basics: Replacing a Command's Behavior, where we finally write a real mock and watch a function run completely offline.