Contributing to an Existing CodebaseLesson 5.3
How to write tests for code you didn't write
testing existing behavior, characterization tests, finding test boundaries, mocking dependencies you don't own, test doubles, testing edge cases in legacy code
Test What It Does, Not What It Should Do
When writing tests for unfamiliar code, your first job isn't to test what the code should do — it's to characterize what it actually does. These are called characterization tests, and they serve as a safety net before refactoring.
Writing a Characterization Test
// You're not sure what parseDateRange does — run it and observe
const result = parseDateRange('2024-01-15', '2024-01-20');
console.log(result);
// Output: { start: Date('2024-01-15'), end: Date('2024-01-20'), days: 5 }
// Now encode what it actually does as a test
test('parseDateRange returns start, end Date objects and day count', () => {
const result = parseDateRange('2024-01-15', '2024-01-20');
expect(result.start).toEqual(new Date('2024-01-15'));
expect(result.end).toEqual(new Date('2024-01-20'));
expect(result.days).toBe(5);
});
// Also test the boundary — what happens with same-day range?
test('parseDateRange with same start and end date returns days: 0', () => {
const result = parseDateRange('2024-01-15', '2024-01-15');
expect(result.days).toBe(0);
});Mocking Dependencies You Don't Own
// If the function calls a DB, mock it
jest.mock('../db/users', () => ({
findById: jest.fn().mockResolvedValue({ id: '1', email: 'a@b.com' })
}));
Test the unit in isolation first. Integration tests verify the whole chain — write them after you understand each piece.
