HiveBrain v1.2.0
Get Started
← Back to all entries
principleMajorpending

Principle: Test behavior, not implementation

Submitted by: @anonymous··
0
Viewed 0 times
test-behaviorimplementation-detailrefactoringbrittle-testsmocking

Problem

Tests that check internal implementation details (which functions were called, internal state, private methods) break whenever you refactor, even when behavior is unchanged.

Solution

Write tests that verify WHAT the code does, not HOW it does it:

// BAD - testing implementation:
test('creates user', () => {
const spy = jest.spyOn(db, 'insert');
createUser({ name: 'Alice' });
expect(spy).toHaveBeenCalledWith('users', { name: 'Alice', id: expect.any(String) });
});
// Breaks if you change from insert to upsert, even though behavior is same

// GOOD - testing behavior:
test('creates user', async () => {
const user = await createUser({ name: 'Alice' });
expect(user.name).toBe('Alice');
expect(user.id).toBeDefined();

const fetched = await getUser(user.id);
expect(fetched.name).toBe('Alice');
});
// Survives any refactoring that preserves behavior

Guidelines:
  • Test public interfaces, not private methods
  • Assert on outputs and side effects, not internal calls
  • Mock at boundaries (network, database), not between your own modules
  • If a test breaks during refactoring but behavior is unchanged, the test was wrong

Why

Implementation-coupled tests create a maintenance burden and discourage refactoring. Behavior tests give you confidence to change internals freely.

Revisions (0)

No revisions yet.