Navigating Large CodebasesLesson 3.3
How to read tests to understand what code is supposed to do
test file location, describe/it block naming, arrange-act-assert pattern, what tests reveal about API, reading test data, edge cases in tests
Tests Are Executable Specifications
The test suite is the best documentation that exists for most codebases — it's documentation that breaks when it's wrong. Read tests before implementation to understand what a module is supposed to do.
Reading a Test to Understand the API
describe('UserService', () => {
describe('createUser', () => {
it('should return a user object with hashed password', async () => {
// ARRANGE: what state is needed
const input = { email: 'test@example.com', password: 'secret123' };
// ACT: call the function under test
const result = await userService.createUser(input);
// ASSERT: what the result must be
expect(result.email).toBe('test@example.com');
expect(result.password).not.toBe('secret123'); // hashed
expect(result.id).toBeDefined();
});
it('should throw DuplicateEmailError when email exists', async () => {
await userService.createUser({ email: 'dupe@example.com', password: 'pw' });
await expect(
userService.createUser({ email: 'dupe@example.com', password: 'other' })
).rejects.toThrow(DuplicateEmailError);
});
});
});
// What you learned without reading createUser:
// - Input: { email, password } — password is stored hashed
// - Output: user object with id, email, hashed password
// - Error: throws DuplicateEmailError on duplicate emailEdge case tests are especially valuable. They document every known failure mode and boundary condition the original author thought about.
