Code without tests is broken by default. You just don't know it yet. Testing gives you confidence to refactor, ship features faster, and sleep peacefully knowing that a 2 AM deployment won't bring down production. This guide covers everything you need to start testing JavaScript effectively.

Types of Tests

  • Unit Tests: Test individual functions or modules in isolation. Fast, cheap, and you should have lots of them.
  • Integration Tests: Test how multiple units work together — API routes, database queries, component interactions.
  • End-to-End (E2E) Tests: Test the full application from the user's perspective — clicking buttons, filling forms, navigating pages.
The Testing Pyramid
         /   E2E Tests   \        ← Few (slow, expensive)
        / Integration Tests \    ← Moderate
       /    Unit Tests        \  ← Many (fast, cheap)

Getting Started with Jest

Setup
# Install Jest
npm install -D jest

# For TypeScript
npm install -D jest ts-jest @types/jest

# Add test script to package.json
"scripts": {
  "test": "jest",
  "test:watch": "jest --watch",
  "test:coverage": "jest --coverage"
}

Writing Your First Test

math.js
export function add(a, b) {
  return a + b;
}

export function divide(a, b) {
  if (b === 0) throw new Error('Cannot divide by zero');
  return a / b;
}
math.test.js
import { add, divide } from './math';

describe('add', () => {
  test('adds two positive numbers', () => {
    expect(add(2, 3)).toBe(5);
  });

  test('handles negative numbers', () => {
    expect(add(-1, -1)).toBe(-2);
  });

  test('handles zero', () => {
    expect(add(0, 5)).toBe(5);
  });
});

describe('divide', () => {
  test('divides two numbers', () => {
    expect(divide(10, 2)).toBe(5);
  });

  test('throws on division by zero', () => {
    expect(() => divide(10, 0)).toThrow('Cannot divide by zero');
  });
});

Common Matchers

Jest Matchers
// Equality
expect(value).toBe(42);              // Strict equality (===)
expect(obj).toEqual({ a: 1, b: 2 }); // Deep equality

// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();

// Numbers
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(10);
expect(0.1 + 0.2).toBeCloseTo(0.3);  // Floating point

// Strings
expect(str).toMatch(/regex/);
expect(str).toContain('substring');

// Arrays
expect(arr).toContain('item');
expect(arr).toHaveLength(3);

// Objects
expect(obj).toHaveProperty('name');
expect(obj).toMatchObject({ name: 'Alice' });

Mocking

Mocking Functions & Modules
// Mock a function
const mockFn = jest.fn();
mockFn.mockReturnValue(42);
mockFn.mockResolvedValue({ id: 1 }); // For async

// Verify calls
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2');
expect(mockFn).toHaveBeenCalledTimes(3);

// Mock a module
jest.mock('./database', () => ({
  getUser: jest.fn().mockResolvedValue({ id: 1, name: 'Alice' }),
  saveUser: jest.fn().mockResolvedValue(true),
}));

Testing Async Code

Async Tests
// async/await (recommended)
test('fetches user data', async () => {
  const user = await getUser(1);
  expect(user.name).toBe('Alice');
});

// Testing rejected promises
test('throws for invalid ID', async () => {
  await expect(getUser(-1)).rejects.toThrow('Invalid ID');
});

Test-Driven Development (TDD)

  • Red: Write a failing test first
  • Green: Write the minimum code to make it pass
  • Refactor: Improve the code while keeping tests green

TDD forces you to think about the interface before the implementation. The result is code that is inherently testable and well-designed.

Best Practices

  • Test behavior, not implementation. If you refactor internals, tests shouldn't break.
  • One assertion per test concept. Tests should fail for one reason only.
  • Use descriptive test names. "should return 404 when user not found" is better than "test getUser".
  • Aim for 80%+ code coverage but don't chase 100% — diminishing returns.
  • Run tests in CI. Every pull request should pass all tests before merging.
  • Keep tests fast. Slow tests don't get run. Mock external dependencies.

Try Our Free JavaScript Tools

Minify and beautify your JavaScript test files.