Test Basics & Matchers
Test Basics & Matchers Structure // Organizing tests describe('UserService', () => { describe('create', () => { it('should create a user with valid data', () =>…
Test Basics & Matchers
Structure
// Organizing tests
describe('UserService', () => {
describe('create', () => {
it('should create a user with valid data', () => { ... });
it('should throw when email is invalid', () => { ... });
});
describe('delete', () => {
test('removes the user from db', () => { ... });
});
});
// Setup and teardown
beforeAll(async () => {
await db.connect(); // runs once before all tests in file
});
afterAll(async () => {
await db.disconnect(); // runs once after all tests in file
});
beforeEach(() => {
jest.clearAllMocks(); // before each test in the block
});
afterEach(async () => {
await db.rollback(); // after each test in the block
});Matchers
// Equality
expect(value).toBe(42); // strict equality (Object.is)
expect(obj).toEqual({ a: 1 }); // deep equality
expect(obj).toStrictEqual({ a: 1 }); // deep equality + type check (undefined vs missing)
expect(obj).not.toBe(other);
// Truthiness
expect(value).toBeTruthy();
expect(value).toBeFalsy();
expect(value).toBeNull();
expect(value).toBeUndefined();
expect(value).toBeDefined();
// Numbers
expect(n).toBeGreaterThan(5);
expect(n).toBeGreaterThanOrEqual(5);
expect(n).toBeLessThan(10);
expect(0.1 + 0.2).toBeCloseTo(0.3, 5); // floating point
// Strings
expect(str).toContain('substring');
expect(str).toMatch(/regex/);
expect(str).toMatch('substring');
expect(str).toHaveLength(5);
// Arrays
expect(arr).toContain(42);
expect(arr).toContainEqual({ id: 1 }); // deep
expect(arr).toHaveLength(3);
expect(arr).toEqual(expect.arrayContaining([1, 2])); // subset
// Objects
expect(obj).toHaveProperty('user.name');
expect(obj).toHaveProperty('count', 5);
expect(obj).toMatchObject({ status: 'ok' }); // partial match
// Errors
expect(() => fn()).toThrow();
expect(() => fn()).toThrow('error message');
expect(() => fn()).toThrow(TypeError);
await expect(asyncFn()).rejects.toThrow('error');
await expect(asyncFn()).rejects.toMatchObject({ statusCode: 404 });
// Asymmetric matchers
expect(user).toEqual({
id: expect.any(Number),
email: expect.stringContaining('@'),
roles: expect.arrayContaining(['user']),
createdAt: expect.any(Date),
});Async Tests
// Async/await — preferred
it('should fetch user', async () => {
const user = await getUser(1);
expect(user.name).toBe('Alice');
});
// Promises
it('should fetch user', () => {
return getUser(1).then(user => {
expect(user.name).toBe('Alice');
});
});
// resolves/rejects matchers
it('should resolve', async () => {
await expect(getUser(1)).resolves.toMatchObject({ name: 'Alice' });
});
it('should reject', async () => {
await expect(getUser(-1)).rejects.toThrow('Not found');
});
// Timeout override
it('slow test', async () => {
await slowOperation();
}, 10000); // 10 second timeoutTest Configuration
// jest.config.ts
export default {
preset: 'ts-jest',
testEnvironment: 'node', // or 'jsdom' for browser
setupFilesAfterFramework: ['<rootDir>/jest.setup.ts'],
collectCoverage: true,
coverageThreshold: {
global: { branches: 70, functions: 80, lines: 80, statements: 80 }
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // path aliases
},
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts', '**/*.spec.ts'],
};
// package.json scripts
{
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:ci": "jest --ci --coverage --reporters=default --reporters=jest-junit"
}