
Most apps we build today are not stand-alone.
They need to talk to something outside, like a payment gateway, an auth provider, a third-party API, or even another microservice inside the company.
That creates a problem when it comes to testing. You don’t want your tests to hit real services, because they are slow, unreliable, and in some cases cost money.
So we mock.
But mocking comes with its own traps. If you are not careful, the fake stuff can bleed into production or, on the other side, your tests might start hitting real services. Both situations are bad news.
Why bother mocking
- Tests run faster because you don’t have to wait on network calls.
- Your test suite becomes more reliable since you don’t depend on someone else’s uptime.
- You don’t burn through credits or get throttled on APIs.
- You can focus on testing your own logic instead of testing the external system.
What goes wrong
Here are a few ways things usually break:
Mocks inside production code
I’ve seen projects where people directly put their fake Stripe or fake S3 client inside the same folder as the real implementation. Sooner or later someone forgets to switch it back.Environment confusion
A missing or misconfiguredNODE_ENVcan suddenly make your app use mocks in production or real services in CI.Tests that depend on external state
For example, “this test assumes a user exists in the sandbox account.” That works once, then fails when the sandbox gets wiped.Shared config between test and prod
Having a single axios/fetch wrapper that conditionally decides whether to mock or not often gets messy.
How to keep it clean
Keep mocks out of prod
Only load mocks in test or dev code. Put them in (__mocks__ ) or a dedicated test/mocks folder. Never import them from main app code.
Be explicit about environments
Check for NODE_ENV or whatever flag you use. Do not silently fall back to a mock. If you are not in test or development, throw an error.
if (process.env.NODE_ENV === "test") {
require("./test/mocks/stripe");
}Use the right tools
- On the backend you can use
nock,jest.mock, or MSW in node mode. - On the frontend, MSW works well because it intercepts fetch/XHR without touching your app code.
Don’t share real keys in tests
Always use fake or test keys. If a test ever hits prod with a real key, it should fail immediately.
Add safety nets in CI
You can block all network calls in CI unless they are explicitly mocked.
beforeAll(() => {
jest.spyOn(global, "fetch").mockImplementation(() => {
throw new Error("Unexpected network call in test");
});
});A layered approach
Think in layers:
- Unit tests use mocks and fakes.
- Integration tests hit sandbox or local test servers.
- End to end tests run against staging with safe test data.
This way you get coverage without risking production.
Closing thoughts
Mocking is important, but it’s not something you can just sprinkle on. If you do it carelessly, you either end up with production talking to fake services or your test suite draining money by hitting real APIs. Keep your environments separate, set up strict checks, and fail loudly if something looks off.
The goal is simple: test with confidence without putting production at risk.