Introduction to Unit Testing

What is Unit Testing?

Unit testing involves testing the smallest testable parts of an application (units) in isolation. A “unit” typically refers to:

Why Unit Testing?

Benefits:

Types of Software Testing

Test Type Scope Speed Example
Unit Tests Individual functions/methods Fast (milliseconds) Test a calculateTotal() function
Integration Tests Multiple components working together Medium (seconds) Test API endpoint with database
End-to-End Tests Entire application flow Slow (minutes) Test complete user registration flow

The Testing Pyramid:

        /\
       /E2E\       ← Few (slowest, most expensive)
      /------\
     /  Int.  \    ← Some (medium speed & cost)
    /----------\
   /   Unit     \  ← Many (fastest, cheapest)
  /--------------\

Unit tests should form the foundation of your testing strategy.


Setting Up Jest with TypeScript

Installation

First, install Jest and the necessary TypeScript dependencies:

npm install --save-dev jest @types/jest ts-jest typescript

Configuration

TypeScript Configuration

Create a tsconfig.json file in your project root:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "types": ["jest", "node"]
  },
  "include": ["src/**/*", "tests/**/*"],
  "exclude": ["node_modules", "dist"]
}

Key configuration notes:

Jest Configuration

Create a jest.config.js file in your project root:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src', '<rootDir>/tests'],
  testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
  ],
};

Project Structure

Organize your tests in a separate test directory

src/
  math.ts
tests/
  math.test.ts

Package.json Scripts

Add test scripts to your package.json:

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

Writing Your First Test

Simple Function Example

File: src/math.ts

export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

File: src/math.test.ts

import { add, subtract } from '../src/math';

describe('Math functions', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(add(1, 2)).toBe(3);
  });

  test('adds -1 + 1 to equal 0', () => {
    expect(add(-1, 1)).toBe(0);
  });

  test('subtracts 5 - 3 to equal 2', () => {
    expect(subtract(5, 3)).toBe(2);
  });
});

Running Tests

npm test

Expected output:

PASS  src/math.test.ts
  Math functions
    ✓ adds 1 + 2 to equal 3 (2 ms)
    ✓ adds -1 + 1 to equal 0 (1 ms)
    ✓ subtracts 5 - 3 to equal 2 (1 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total

Tips for Writing Good Tests

  1. Test behavior, not implementation
  2. Keep tests simple and focused
  3. Use descriptive test names
  4. Don’t repeat yourself (use helper functions)
  5. Test edge cases and error conditions
  6. Keep tests fast
  7. Make tests deterministic (no random values)