Test Stub
In short
A test stub is a stand-in for a real dependency that returns prepared answers during a test, so code can be tested without the real database or service.
What is a test stub?
A test stub replaces a dependency of the code under test, such as an API client, a database query or the system clock, with a simple object that returns canned answers: values written into the test in advance. When the code asks the stub for an exchange rate, it gets 0.9; when it asks for the date, it gets January 1. The test then checks what the code did with those answers.
Stubs let a test control the indirect inputs of the code, meaning the data it receives from the objects it calls rather than through its own arguments. That makes it easy to create situations that are hard to trigger for real: a payment gateway that declines a card, an API that times out, a search that finds nothing. A stub is like a stand-in reading fixed lines from a script at a rehearsal, so the lead actor can practice the scene; nobody grades the stand-in.
The term comes from Gerard Meszaros's 2007 book xUnit Test Patterns, which names five kinds of test double: dummy, stub, spy, mock and fake. You can write a stub by hand as a small object, or create one with a library: Mockito's when(...).thenReturn(...), Jest's mockReturnValue, Sinon's sinon.stub() and Python's Mock(return_value=...) all create stubs, even though these tools often call everything a mock.
Stubs and mocks are the pair most often confused. A stub only supplies answers, and the test asserts on the result or the state of the code under test; a mock also carries expectations about how it is called, and the test fails if, say, sendEmail wasn't called exactly once. In Martin Fowler's terms, stubs support state verification and mocks support behavior verification. Prefer stubs when only the outcome matters, because tests that check every call tend to break whenever the implementation changes.
Key takeaways
- A test stub replaces a real dependency and returns canned answers.
- It controls the indirect inputs of the code under test, including hard-to-trigger errors.
- A stub never fails a test by itself; the test asserts on the result.
- A mock also verifies how it was called; a stub doesn't.
- Dummies, stubs, spies, mocks and fakes are all test doubles.
Example
// The code under test asks a rates service for the current exchange rate
function toEuros(amountUsd, ratesService) {
return amountUsd * ratesService.getRate("USD", "EUR");
}
// The stub: no network, no logic, just a canned answer
const ratesStub = { getRate: () => 0.9 };
test("converts dollars to euros", () => {
expect(toEuros(100, ratesStub)).toBe(90); // check the result, not the calls
});
// A mock would also check that getRate was called with ("USD", "EUR").Readers ask
What is the difference between a stub and a mock?
A stub returns prepared answers so the code under test can run, and the test then checks the result. A mock also records its calls and checks them against expectations, so the test can fail because the code called it the wrong way.
What is the difference between a stub and a fake?
A stub returns fixed answers and has no real logic. A fake is a working but simplified implementation, such as an in-memory database that really stores and finds records, and suits tests that need realistic behavior across many calls.
When should I use a stub?
When the code under test needs data from a slow, unreliable or costly dependency, or when you need a specific answer such as an error or an empty list. If the outcome that matters is a call to another system, such as sending an email, a mock or a spy fits better.
See also
- MockingTesting & Quality, p. 16Mocking is a testing technique that replaces a real dependency, such as a database or API, with a fake you control so that code can be tested in isolation.
- Unit TestTesting & Quality, p. 36A unit test is a small, automated check that verifies one function, method, or class behaves correctly in isolation from the rest of the program.
- Dependency InjectionSoftware Architecture, p. 13Dependency injection is a design technique in which an object receives the other objects it needs from the outside instead of creating them itself.
- Test FixtureTesting & Quality, p. 31A test fixture is the known state a test needs before it runs, such as sample data or a configured object, plus the code that sets it up and tears it down.
- Integration TestTesting & Quality, p. 12An integration test is an automated test that checks whether several parts of a system, such as code, a database, and an API, work correctly together.
- Test-Driven DevelopmentTesting & Quality, p. 35Test-driven development is a coding practice in which you write a failing test first, then write just enough code to pass it, and then clean up the design.
Sources
Spotted a mistake or something missing on this page?Suggest an edit