TypeScript Testing (Jest/Mocha)

Writing and running tests for TypeScript applications

๐Ÿงช What is TypeScript Testing?

Testing ensures your TypeScript code works correctly. Jest and Mocha are popular testing frameworks that help you write automated tests, catch bugs early, and maintain code quality with type-safe test suites.


// Simple test example
function add(a: number, b: number): number {
  return a + b;
}

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

Testing Frameworks

๐Ÿƒ

Jest

All-in-one testing framework

describe('Calculator', () => {
  test('adds numbers', () => {
    expect(2 + 2).toBe(4);
  });
});
โ˜•

Mocha

Flexible testing framework

describe('Calculator', () => {
  it('adds numbers', () => {
    assert.equal(2 + 2, 4);
  });
});
โœ…

Assertions

Verify expected outcomes

expect(value).toBe(5);
expect(array).toContain('item');
expect(obj).toHaveProperty('key');
๐ŸŽญ

Mocking

Simulate dependencies

const mockFn = jest.fn();
mockFn.mockReturnValue(42);
expect(mockFn()).toBe(42);

๐Ÿ”น Setting Up Jest

Install Jest for TypeScript:

# Install Jest and TypeScript support
npm install --save-dev jest @types/jest ts-jest

# Initialize Jest configuration
npx ts-jest config:init

Create jest.config.js :

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

๐Ÿ”น Writing Jest Tests

Create a test file calculator.test.ts :

// calculator.ts
export class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }

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

  multiply(a: number, b: number): number {
    return a * b;
  }
}

// calculator.test.ts
import { Calculator } from './calculator';

describe('Calculator', () => {
  let calculator: Calculator;

  beforeEach(() => {
    calculator = new Calculator();
  });

  test('should add two numbers', () => {
    expect(calculator.add(2, 3)).toBe(5);
  });

  test('should subtract two numbers', () => {
    expect(calculator.subtract(5, 3)).toBe(2);
  });

  test('should multiply two numbers', () => {
    expect(calculator.multiply(4, 3)).toBe(12);
  });
});

๐Ÿ”น Setting Up Mocha

Install Mocha with TypeScript:

# Install Mocha, Chai, and TypeScript support
npm install --save-dev mocha @types/mocha chai @types/chai ts-node

Create .mocharc.json :

{
  "require": ["ts-node/register"],
  "extensions": ["ts"],
  "spec": ["src/**/*.test.ts"],
  "watch-files": ["src"]
}

๐Ÿ”น Writing Mocha Tests

Create a test file with Mocha and Chai:

// user.ts
export interface User {
  id: number;
  name: string;
  email: string;
}

export class UserService {
  private users: User[] = [];

  addUser(user: User): void {
    this.users.push(user);
  }

  getUser(id: number): User | undefined {
    return this.users.find(u => u.id === id);
  }

  getAllUsers(): User[] {
    return this.users;
  }
}

// user.test.ts
import { expect } from 'chai';
import { UserService, User } from './user';

describe('UserService', () => {
  let userService: UserService;

  beforeEach(() => {
    userService = new UserService();
  });

  it('should add a user', () => {
    const user: User = { id: 1, name: 'John', email: '[email protected]' };
    userService.addUser(user);
    expect(userService.getAllUsers()).to.have.lengthOf(1);
  });

  it('should get user by id', () => {
    const user: User = { id: 1, name: 'John', email: '[email protected]' };
    userService.addUser(user);
    const found = userService.getUser(1);
    expect(found).to.deep.equal(user);
  });

  it('should return undefined for non-existent user', () => {
    const found = userService.getUser(999);
    expect(found).to.be.undefined;
  });
});

๐Ÿ”น Common Jest Matchers

Useful assertions for testing:

// Equality
expect(value).toBe(5);                    // Strict equality
expect(obj).toEqual({ name: 'John' });    // Deep equality

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

// Numbers
expect(value).toBeGreaterThan(3);
expect(value).toBeLessThanOrEqual(10);

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

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

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

๐Ÿ”น Async Testing

Test asynchronous code:

// Async function to test
async function fetchUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

// Jest async test
test('fetches user data', async () => {
  const user = await fetchUser(1);
  expect(user.id).toBe(1);
  expect(user.name).toBeDefined();
});

// Mocha async test
it('fetches user data', async () => {
  const user = await fetchUser(1);
  expect(user.id).to.equal(1);
  expect(user.name).to.exist;
});

๐Ÿ”น Mocking with Jest

Mock functions and modules:

// Mock a function
const mockCallback = jest.fn((x: number) => x * 2);

test('mock function', () => {
  mockCallback(2);
  mockCallback(4);
  
  expect(mockCallback).toHaveBeenCalledTimes(2);
  expect(mockCallback).toHaveBeenCalledWith(2);
  expect(mockCallback(3)).toBe(6);
});

// Mock a module
jest.mock('./api');
import { fetchData } from './api';

test('mocked API call', async () => {
  (fetchData as jest.Mock).mockResolvedValue({ data: 'test' });
  
  const result = await fetchData();
  expect(result.data).toBe('test');
});

๐Ÿ”น Running Tests

Add test scripts to package.json :

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

Run tests:

# Run all tests
npm test

# Watch mode (re-run on changes)
npm run test:watch

# Generate coverage report
npm run test:coverage

๐Ÿง  Test Your Knowledge

What is the purpose of the beforeEach() function in tests?