Safeguard
Developer Tools•

Mocks hide security bugs: what to fake and what to keep real

Mocking the auth layer, the database or the HTTP client can make a test pass while production is broken. A guide to where mocks are safe and where not.

Safeguard Security Team
3 min read

Mock the things that are slow, paid or outside your control, such as a payment provider. Keep real the things that enforce security: the authorization check, input validation, the database constraints and the serializer. A test that mocks the guard cannot find a missing guard.

The pattern behind the problem

Suppose a handler calls requireRole("admin"). In a unit test, that function is mocked to return true so the test can focus on the business logic. The test passes forever, even after someone deletes the call from the handler, because the mock never mattered.

Mocking is not wrong. Mocking the control under test is.

Safe to fake

  • Third party APIs you do not own, replaced with a stub that returns recorded responses.
  • The clock, so you can test expiry without waiting.
  • Random sources, when you need repeatable output. Keep real randomness in the production path.
  • Email and SMS sending, replaced by a recorder you can inspect.

Keep real, or test at a higher level

  • Authentication middleware and the permission function.
  • Validation schemas and output encoding.
  • Database constraints, which an in-memory fake may not enforce.
  • Signature verification on webhooks. Use a real signature computed with a test key.

Make fakes honest

A fake should fail the way the real thing fails. If the real payment API returns an error for a repeated idempotency key, so should your stub. If it times out, give the stub a way to time out.

Verify fakes against the real service on a schedule. This is where contract tests help: they record what the consumer expects and check the provider still meets it. Pact's documentation describes the consumer as the client that wants data and the provider as the API that supplies it, with the contract generated while the consumer's automated tests run. Our earlier post on consumer driven contract tests covers the security angle at /resources/blog/consumer-driven-contract-tests-security-checks.

Mocking the clock and the network

Two more places to be careful. A mocked clock that never moves can hide expiry bugs, so move it in at least one test. A mocked HTTP client that always returns success hides the error handling that matters most when a dependency fails: does your code fail closed, or does it treat a timeout as permission? Add one stub that returns an error and one that hangs, and assert the secure outcome in both.

A quick audit of your own suite

Search the tests for mocks of anything named auth, permission, policy, validate or verify. For each hit, ask: is there another test that proves this control is actually enforced on the route? If the answer is no, add one integration test that calls the route without credentials.

Also delete a guard in a scratch branch and run the suite. If everything stays green, you have found the gap.

Quick check

Are mocks bad practice?

No. They keep tests fast and isolate failures. The rule is to avoid replacing the security control that the test is supposed to protect.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.