// Installation
npm install --save-dev jest
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
// math.js
export const add = (a, b) => a + b;
export const multiply = (a, b) => a * b;
// math.test.js
import { add, multiply } from './math';
describe('Math functions', () => {
test('adds two numbers correctly', () => {
expect(add(2, 3)).toBe(5);
expect(add(-1, 1)).toBe(0);
expect(add(0, 0)).toBe(0);
});
test('multiplies two numbers correctly', () => {
expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
expect(multiply(0, 5)).toBe(0);
});
});
// async.test.js
test('async function', async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
// mock.test.js
jest.mock('./api');
import { fetchUser } from './api';
test('mocking API call', async () => {
fetchUser.mockResolvedValue({ id: 1, name: 'John' });
const user = await fetchUser(1);
expect(user.name).toBe('John');
expect(fetchUser).toHaveBeenCalledWith(1);
});
// Installation
npm install --save-dev mocha chai
// test/math.test.js
const { expect } = require('chai');
const { add, multiply } = require('../src/math');
describe('Math functions', () => {
describe('add()', () => {
it('should add two positive numbers', () => {
expect(add(2, 3)).to.equal(5);
});
it('should handle negative numbers', () => {
expect(add(-1, 1)).to.equal(0);
});
it('should handle zero', () => {
expect(add(0, 0)).to.equal(0);
});
});
describe('multiply()', () => {
it('should multiply two numbers', () => {
expect(multiply(2, 3)).to.equal(6);
});
it('should handle negative numbers', () => {
expect(multiply(-2, 3)).to.equal(-6);
});
});
});
// test/async.test.js
const { fetchData } = require('../src/api');
describe('Async operations', () => {
it('should fetch data', async () => {
const data = await fetchData();
expect(data).to.exist;
});
it('should handle errors', async () => {
try {
await fetchData('invalid');
expect.fail('Should have thrown');
} catch (error) {
expect(error).to.exist;
}
});
});
// test/hooks.test.js
describe('Database operations', () => {
before(async () => {
// Setup database connection
await db.connect();
});
beforeEach(async () => {
// Clean data before each test
await db.clear();
});
after(async () => {
// Close database connection
await db.disconnect();
});
it('should save user', async () => {
const user = { name: 'John' };
await db.users.save(user);
const saved = await db.users.findOne({ name: 'John' });
expect(saved).to.deep.equal(user);
});
});
// jest.config.js
module.exports = {
testEnvironment: 'node',
transform: {
'^.+\\.jsx?$': 'babel-jest'
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
// .mocharc.js
module.exports = {
require: ['@babel/register', './test/setup.js'],
reporter: 'spec',
timeout: 5000,
file: ['./test/setup.js']
};
# Jest
npm test # Exécuter les tests
npm test -- --watch # Mode watch
npm test -- --coverage # Rapport de couverture
# Mocha
mocha # Exécuter les tests
mocha --watch # Mode watch
mocha --reporter dot # Changer le format de sortie
// Button.test.jsx
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button component', () => {
test('renders with text', () => {
const { getByText } = render(
<Button>Click me</Button>
);
expect(getByText('Click me')).toBeInTheDocument();
});
test('calls onClick when clicked', () => {
const onClick = jest.fn();
const { getByText } = render(
<Button onClick={onClick}>Click me</Button>
);
fireEvent.click(getByText('Click me'));
expect(onClick).toHaveBeenCalled();
});
});
// Form.test.jsx
import { render, fireEvent, waitFor } from '@testing-library/react';
import Form from './Form';
describe('Form component', () => {
test('submits form data', async () => {
const onSubmit = jest.fn();
const { getByLabelText, getByText } = render(
<Form onSubmit={onSubmit} />
);
fireEvent.change(
getByLabelText('Email'),
{ target: { value: 'test@example.com' }}
);
fireEvent.change(
getByLabelText('Password'),
{ target: { value: 'password123' }}
);
fireEvent.click(getByText('Submit'));
await waitFor(() => {
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123'
});
});
});
});
// useCounter.test.js
import { renderHook, act } from '@testing-library/react-hooks';
import useCounter from './useCounter';
describe('useCounter', () => {
test('should increment counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
test('should decrement counter', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.decrement();
});
expect(result.current.count).toBe(-1);
});
});
// useFetch.test.js
import { renderHook } from '@testing-library/react-hooks';
import useFetch from './useFetch';
describe('useFetch', () => {
beforeEach(() => {
fetch.resetMocks();
});
test('should fetch data', async () => {
const mockData = { id: 1, name: 'Test' };
fetch.mockResponseOnce(JSON.stringify(mockData));
const { result, waitForNextUpdate } = renderHook(() =>
useFetch('api/data')
);
expect(result.current.loading).toBe(true);
await waitForNextUpdate();
expect(result.current.data).toEqual(mockData);
expect(result.current.loading).toBe(false);
expect(result.current.error).toBe(null);
});
test('should handle error', async () => {
const error = new Error('API Error');
fetch.mockRejectOnce(error);
const { result, waitForNextUpdate } = renderHook(() =>
useFetch('api/data')
);
expect(result.current.loading).toBe(true);
await waitForNextUpdate();
expect(result.current.data).toBe(null);
expect(result.current.loading).toBe(false);
expect(result.current.error).toEqual(error);
});
});