5 Jest Mistakes That Make Senior Engineers Cringe
I've reviewed hundreds of TypeScript test suites as an architecture coach. These 5 mistakes show up in almost every codebase — and they're all easy to fix.
1. Using as any to Silence Mock Errors
// ❌ You lose all type safety in your tests
const mockService = { getUser: jest.fn() } as any;
// ✅ Use jest.Mocked<T> for full type coverage
const mockService: jest.Mocked<UserService> = {
getUser: jest.fn(),
createUser: jest.fn(),
// TypeScript will error if you miss a method
};When your mocks aren't typed, your tests can pass while your production code is broken. The types ARE the test.
2. Testing Implementation Instead of Behavior
// ❌ This test breaks every time you refactor
it('should call repository.find with correct params', () => {
service.getActiveUsers();
expect(mockRepo.find).toHaveBeenCalledWith({ active: true });
});
// ✅ Test WHAT it returns, not HOW it gets there
it('should return only active users', () => {
mockRepo.find.mockResolvedValue([activeUser, inactiveUser]);
const result = await service.getActiveUsers();
expect(result).toEqual([activeUser]);
});If you refactor the internals and your tests break — your tests are the problem.
3. Forgetting jest.resetAllMocks()
// ❌ Mock state leaks between tests
beforeEach(() => {
mockService = { fetch: jest.fn().mockResolvedValue(data) };
});
// ✅ Reset everything — every time
afterEach(() => {
jest.resetAllMocks();
});Mock state bleed is the #1 cause of flaky tests. One line in afterEach prevents hours of debugging.
4. No Error Path Tests
If your test file only has happy-path tests, you're not testing — you're hoping. Every try/catch, every if (!data), every .catch() in your production code needs a corresponding test.
describe('createUser', () => {
it('should create user with valid input', async () => { /* ✅ */ });
it('should throw on duplicate email', async () => { /* ✅ */ });
it('should throw on invalid input', async () => { /* ✅ */ });
it('should not send email if save fails', async () => { /* ✅ */ });
});5. Chasing 100% Coverage
Coverage measures what code was executed, not what was verified. This test has 100% coverage and tests absolutely nothing:
it('should exist', () => {
const result = service.calculatePrice(100, 0.2);
expect(result).toBeDefined(); // Proves nothing
});Aim for 80-85% coverage with high-quality assertions. That beats 100% coverage with toBeDefined() checks every time.
---
Want to go deeper? The full Jest Mastery course covers mocking patterns, CI/CD integration, dependency injection, and the complete testing decision framework. 15 lessons, zero fluff.
