0018.3 jest testing framework for Node.js

project
false
area
false
resource
false
status
No value
aliases
No value
tags
No value
description
No value
title
0018.3 jest testing framework for Node.js
created
2024-12-04T17:01:11
updated
2025-03-03T22:09:47

Cheatsheet

Installation

npm install --save-dev jest

For TypeScript:

npm install --save-dev ts-jest @types/jest
npx ts-jest config:init

Basic Commands


Test Structure

describe('Group of tests', () => {
  beforeAll(() => {
    // Runs once before all tests
  });

  beforeEach(() => {
    // Runs before each test
  });

  afterEach(() => {
    // Runs after each test
  });

  afterAll(() => {
    // Runs once after all tests
  });

  test('should do something', () => {
    expect(true).toBe(true);
  });
});

Matchers

Common Matchers

expect(value).toBe(value);               // Strict equality
expect(value).toEqual(value);           // Deep equality
expect(value).toEqual(expect.any(constructor)) // Matches anything that was created with the given constructor
expect(value).toBeNull();               // null
expect(value).toBeDefined();            // Not undefined
expect(value).toBeUndefined();          // undefined
expect(value).toBeTruthy();             // true-like
expect(value).toBeFalsy();              // false-like

Numbers

expect(value).toBeGreaterThan(number);
expect(value).toBeGreaterThanOrEqual(number);
expect(value).toBeLessThan(number);
expect(value).toBeLessThanOrEqual(number);
expect(value).toBeCloseTo(number, precision);

Strings

expect(string).toMatch(/regex/);

Arrays/Iterables

expect(array).toContain(item);
expect(array).toHaveLength(number);

Objects

expect(object).toHaveProperty(keyPath, value);
expect(object).toHaveProperty('kitchen.area', 20);

Exceptions

expect(() => {
  throw new Error('error');
}).toThrow('error');

async function to throw

it('should test async errors', async () =>  {        
    await expect(failingAsyncTest())
    .rejects
    .toThrow('I should fail');
});

Mock Functions

Basic Mock

const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();
expoect(mockFn).toHaveBeenCalledWith(...args);
expect(mockFn).toHaveBeenCalledTimes(1);

Mock Implementation

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

const mockFn2 = jest.fn().mockImplementation((x) => x + 1);
expect(mockFn2(1)).toBe(2);

Mock Implementation

// Uses a custom implementation that adds 1
const mockFn3 = jest.fn()
  .mockImplementation((x) => x + 1);

expect(mockFn3(1)).toBe(2);
expect(mockFn3(5)).toBe(6);
// Custom implementation only on the first call, then undefined
const mockFn4 = jest.fn()
  .mockImplementationOnce((x) => x * 2);

expect(mockFn4(3)).toBe(6);
expect(mockFn4(3)).toBeUndefined(); 

Spy on Existing Function

const obj = {
  method: (x) => x + 1,
};
const spy = jest.spyOn(obj, 'method');
obj.method(2);
expect(spy).toHaveBeenCalledWith(2);

Mock Return Values

// Always returns 42
const mockFn = jest.fn().mockReturnValue(42);

expect(mockFn()).toBe(42);
expect(mockFn()).toBe(42); // Still 42
// Returns 10 on the first call, then undefined afterward
const mockFn2 = jest.fn()
  .mockReturnValueOnce(10);

expect(mockFn2()).toBe(10);
expect(mockFn2()).toBeUndefined();

Mocking Asynchronous/Promises

// mockResolvedValue: always resolves to the given value
const fetchData = jest.fn().mockResolvedValue({ data: 'Mocked data' });

test('it resolves with mocked data', async () => {
  const result = await fetchData();
  expect(result).toEqual({ data: 'Mocked data' });
});
// mockResolvedValueOnce: resolves with different values on each call
const fetchData2 = jest.fn()
  .mockResolvedValueOnce({ data: 'Mocked data #1' })
  .mockResolvedValueOnce({ data: 'Mocked data #2' })
  .mockResolvedValue({ data: 'Default mocked data' });

test('returns different values for sequential calls', async () => {
  let result = await fetchData2();
  expect(result).toEqual({ data: 'Mocked data #1' });

  result = await fetchData2();
  expect(result).toEqual({ data: 'Mocked data #2' });

  result = await fetchData2();
  expect(result).toEqual({ data: 'Default mocked data' });
});
// mockRejectedValue: always rejects with an error
const fetchData3 = jest.fn().mockRejectedValue(new Error('Async error'));

test('rejects with an error', async () => {
  await expect(fetchData3()).rejects.toThrow('Async error');
});
// mockRejectedValueOnce: rejects on first call, resolves on subsequent calls
const fetchData4 = jest.fn()
  .mockRejectedValueOnce(new Error('Async error #1'))
  .mockResolvedValue({ data: 'Recovery data' });

test('rejects on first call, resolves on second', async () => {
  await expect(fetchData4()).rejects.toThrow('Async error #1');
  await expect(fetchData4()).resolves.toEqual({ data: 'Recovery data' });
});

Combining Multiple Behaviors

// Chain multiple resolved/rejected calls
const someAsyncFunction = jest.fn()
  .mockResolvedValueOnce('First call')
  .mockRejectedValueOnce(new Error('Second call failed'))
  .mockResolvedValue('All subsequent calls');

Usage in Test Suites

import { fetchUserData } from './dataService'; // The function you're mocking

jest.mock('./dataService', () => ({
  fetchUserData: jest.fn()
}));

describe('test asynchronous data fetch', () => {
  it('successfully fetches data', async () => {
    fetchUserData.mockResolvedValue({ name: 'Alice', age: 25 });
    
    const result = await fetchUserData();
    expect(result).toEqual({ name: 'Alice', age: 25 });
    expect(fetchUserData).toHaveBeenCalledTimes(1);
  });

  it('handles errors gracefully', async () => {
    fetchUserData.mockRejectedValue(new Error('Network issue'));
    
    try {
      await fetchUserData();
      throw new Error('This test should have failed but did not');
    } catch (error) {
      expect(error).toBeInstanceOf(Error);
      expect(error.message).toBe('Network issue');
    }
  });
});

Async Testing

Testing Promises

test('promise resolves', async () => {
  await expect(Promise.resolve('value')).resolves.toBe('value');
});

Testing Async/Await

test('async/await', async () => {
  const data = await fetchData();
  expect(data).toBe('value');
});

Testing Callbacks

test('callback test', (done) => {
  function callback(data) {
    expect(data).toBe('value');
    done();
  }
  asyncOperation(callback);
});

Snapshots

Creating Snapshots

test('matches snapshot', () => {
  const tree = render(<Component />);
  expect(tree).toMatchSnapshot();
});

Updating Snapshots

npx jest --updateSnapshot

Mock Modules

Mock Entire Module

jest.mock('module-name');

Mock Specific Function

jest.mock('module-name', () => ({
  functionName: jest.fn(),
}));

Clear/Reset Mocks

jest.clearAllMocks();
jest.resetAllMocks();
jest.restoreAllMocks();

Configuring Jest

jest.config.js

module.exports = {
  testEnvironment: 'node', // or 'jsdom'
  transform: {
    '^.+\\.jsx?

#### TypeScript Configuration

Update `jest.config.js`:

```javascript
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};

Coverage

Collect Coverage

npx jest --coverage

Exclude Files in Config

module.exports = {
  collectCoverage: true,
  collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'],
};

Debugging

Run Jest with Node debugger:

node --inspect-brk ./node_modules/.bin/jest --runInBand

: 'babel-jest',
},
moduleNameMapper: {
'\.(css|less)

TypeScript Configuration

Update jest.config.js:

{{CODE_BLOCK_36}}


Coverage

Collect Coverage

{{CODE_BLOCK_37}}

Exclude Files in Config

{{CODE_BLOCK_38}}


Debugging

Run Jest with Node debugger:

{{CODE_BLOCK_39}}


: '/mocks/styleMock.js',
},
};

#### TypeScript Configuration

Update `jest.config.js`:

{{CODE_BLOCK_36}}

---

### **Coverage**

#### Collect Coverage

{{CODE_BLOCK_37}}

#### Exclude Files in Config

{{CODE_BLOCK_38}}

---

### **Debugging**

Run Jest with Node debugger:

{{CODE_BLOCK_39}}

---