jest-testing
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseJest Testing Skill
Jest测试技能
Master testing Node.js applications with Jest - the delightful JavaScript testing framework.
掌握使用Jest测试Node.js应用的方法——Jest是一款便捷的JavaScript测试框架。
Quick Start
快速入门
Test in 3 steps:
- Install -
npm install --save-dev jest supertest - Write Test - Create files
*.test.js - Run -
npm test
3步完成测试:
- 安装 -
npm install --save-dev jest supertest - 编写测试用例 - 创建文件
*.test.js - 运行测试 -
npm test
Core Concepts
核心概念
Basic Test Structure
基础测试结构
javascript
// sum.test.js
const sum = require('./sum');
describe('sum function', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds negative numbers', () => {
expect(sum(-1, -2)).toBe(-3);
});
});javascript
// sum.test.js
const sum = require('./sum');
describe('sum function', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
test('adds negative numbers', () => {
expect(sum(-1, -2)).toBe(-3);
});
});Jest Configuration
Jest配置
javascript
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "node",
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80
}
}
}
}javascript
// package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
},
"jest": {
"testEnvironment": "node",
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80
}
}
}
}Unit Testing
单元测试
javascript
// userService.test.js
const UserService = require('./userService');
const User = require('./models/User');
jest.mock('./models/User');
describe('UserService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('createUser', () => {
it('should create user successfully', async () => {
const userData = {
name: 'John',
email: 'john@example.com'
};
User.create.mockResolvedValue({ id: 1, ...userData });
const result = await UserService.createUser(userData);
expect(User.create).toHaveBeenCalledWith(userData);
expect(result.id).toBe(1);
});
it('should throw error for duplicate email', async () => {
User.create.mockRejectedValue(new Error('Email exists'));
await expect(UserService.createUser({}))
.rejects
.toThrow('Email exists');
});
});
});javascript
// userService.test.js
const UserService = require('./userService');
const User = require('./models/User');
jest.mock('./models/User');
describe('UserService', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('createUser', () => {
it('should create user successfully', async () => {
const userData = {
name: 'John',
email: 'john@example.com'
};
User.create.mockResolvedValue({ id: 1, ...userData });
const result = await UserService.createUser(userData);
expect(User.create).toHaveBeenCalledWith(userData);
expect(result.id).toBe(1);
});
it('should throw error for duplicate email', async () => {
User.create.mockRejectedValue(new Error('Email exists'));
await expect(UserService.createUser({}))
.rejects
.toThrow('Email exists');
});
});
});Learning Path
学习路径
Beginner (1-2 weeks)
入门阶段(1-2周)
- ✅ Setup Jest and write basic tests
- ✅ Understand test structure (describe/it/expect)
- ✅ Learn matchers (toBe, toEqual, etc.)
- ✅ Test synchronous functions
- ✅ 搭建Jest环境并编写基础测试用例
- ✅ 理解测试结构(describe/it/expect)
- ✅ 学习匹配器(toBe, toEqual等)
- ✅ 测试同步函数
Intermediate (3-4 weeks)
进阶阶段(3-4周)
- ✅ Test async functions
- ✅ Mock modules and functions
- ✅ API testing with Supertest
- ✅ Code coverage reports
- ✅ 测试异步函数
- ✅ Mock模块和函数
- ✅ 使用Supertest进行API测试
- ✅ 生成代码覆盖率报告
Advanced (5-6 weeks)
高级阶段(5-6周)
- ✅ Integration testing
- ✅ Test database operations
- ✅ CI/CD integration
- ✅ Performance testing
- ✅ 集成测试
- ✅ 测试数据库操作
- ✅ CI/CD集成
- ✅ 性能测试
API Testing with Supertest
使用Supertest进行API测试
javascript
const request = require('supertest');
const app = require('./app');
describe('User API', () => {
describe('POST /api/users', () => {
it('should create new user', async () => {
const userData = {
name: 'John',
email: 'john@example.com',
password: 'password123'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect('Content-Type', /json/)
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.email).toBe(userData.email);
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({ email: 'invalid' })
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
describe('GET /api/users/:id', () => {
it('should return user by id', async () => {
const response = await request(app)
.get('/api/users/123')
.expect(200);
expect(response.body.id).toBe('123');
});
it('should return 404 for non-existent user', async () => {
await request(app)
.get('/api/users/999')
.expect(404);
});
});
});javascript
const request = require('supertest');
const app = require('./app');
describe('User API', () => {
describe('POST /api/users', () => {
it('should create new user', async () => {
const userData = {
name: 'John',
email: 'john@example.com',
password: 'password123'
};
const response = await request(app)
.post('/api/users')
.send(userData)
.expect('Content-Type', /json/)
.expect(201);
expect(response.body).toHaveProperty('id');
expect(response.body.email).toBe(userData.email);
});
it('should return 400 for invalid email', async () => {
const response = await request(app)
.post('/api/users')
.send({ email: 'invalid' })
.expect(400);
expect(response.body).toHaveProperty('error');
});
});
describe('GET /api/users/:id', () => {
it('should return user by id', async () => {
const response = await request(app)
.get('/api/users/123')
.expect(200);
expect(response.body.id).toBe('123');
});
it('should return 404 for non-existent user', async () => {
await request(app)
.get('/api/users/999')
.expect(404);
});
});
});Mocking Patterns
Mock模式
javascript
// Mock entire module
jest.mock('axios');
const axios = require('axios');
test('fetches data from API', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const result = await fetchUser(1);
expect(axios.get).toHaveBeenCalledWith('/api/users/1');
expect(result.id).toBe(1);
});
// Spy on function
test('calls callback', () => {
const callback = jest.fn();
processData('test', callback);
expect(callback).toHaveBeenCalledWith('test');
expect(callback).toHaveBeenCalledTimes(1);
});
// Mock timers
jest.useFakeTimers();
test('delays execution', () => {
const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
});javascript
// Mock整个模块
jest.mock('axios');
const axios = require('axios');
test('fetches data from API', async () => {
axios.get.mockResolvedValue({ data: { id: 1 } });
const result = await fetchUser(1);
expect(axios.get).toHaveBeenCalledWith('/api/users/1');
expect(result.id).toBe(1);
});
// 监听函数
test('calls callback', () => {
const callback = jest.fn();
processData('test', callback);
expect(callback).toHaveBeenCalledWith('test');
expect(callback).toHaveBeenCalledTimes(1);
});
// Mock定时器
jest.useFakeTimers();
test('delays execution', () => {
const callback = jest.fn();
setTimeout(callback, 1000);
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalled();
});Test Lifecycle Hooks
测试生命周期钩子
javascript
describe('User Tests', () => {
beforeAll(async () => {
// Setup test database
await connectTestDB();
});
afterAll(async () => {
// Cleanup
await disconnectTestDB();
});
beforeEach(async () => {
// Clear data before each test
await User.deleteMany({});
});
afterEach(() => {
// Cleanup after each test
jest.clearAllMocks();
});
test('...', () => {});
});javascript
describe('User Tests', () => {
beforeAll(async () => {
// 搭建测试数据库
await connectTestDB();
});
afterAll(async () => {
// 清理资源
await disconnectTestDB();
});
beforeEach(async () => {
// 每次测试前清理数据
await User.deleteMany({});
});
afterEach(() => {
// 每次测试后清理
jest.clearAllMocks();
});
test('...', () => {});
});Jest Matchers
Jest匹配器
javascript
// Equality
expect(value).toBe(expected) // Strict equality (===)
expect(value).toEqual(expected) // Deep equality
expect(value).not.toBe(expected) // Negation
// Truthiness
expect(value).toBeDefined()
expect(value).toBeNull()
expect(value).toBeTruthy()
expect(value).toBeFalsy()
// Numbers
expect(value).toBeGreaterThan(3)
expect(value).toBeGreaterThanOrEqual(3)
expect(value).toBeLessThan(5)
expect(value).toBeCloseTo(0.3) // Floating point
// Strings
expect(string).toMatch(/pattern/)
expect(string).toContain('substring')
// Arrays
expect(array).toContain(item)
expect(array).toHaveLength(3)
// Objects
expect(obj).toHaveProperty('key')
expect(obj).toMatchObject({ key: 'value' })
// Exceptions
expect(() => fn()).toThrow()
expect(() => fn()).toThrow('error message')
// Async
await expect(promise).resolves.toBe(value)
await expect(promise).rejects.toThrow()javascript
// 相等性
expect(value).toBe(expected) // 严格相等(===)
expect(value).toEqual(expected) // 深度相等
expect(value).not.toBe(expected) // 取反
// 真值判断
expect(value).toBeDefined()
expect(value).toBeNull()
expect(value).toBeTruthy()
expect(value).toBeFalsy()
// 数字
expect(value).toBeGreaterThan(3)
expect(value).toBeGreaterThanOrEqual(3)
expect(value).toBeLessThan(5)
expect(value).toBeCloseTo(0.3) // 浮点数比较
// 字符串
expect(string).toMatch(/pattern/)
expect(string).toContain('substring')
// 数组
expect(array).toContain(item)
expect(array).toHaveLength(3)
// 对象
expect(obj).toHaveProperty('key')
expect(obj).toMatchObject({ key: 'value' })
// 异常
expect(() => fn()).toThrow()
expect(() => fn()).toThrow('error message')
// 异步操作
await expect(promise).resolves.toBe(value)
await expect(promise).rejects.toThrow()Code Coverage
代码覆盖率
bash
undefinedbash
undefinedRun with coverage
带覆盖率报告运行测试
npm test -- --coverage
npm test -- --coverage
Coverage report shows:
覆盖率报告包含:
- Statements: % of code executed
- Statements: 已执行代码的百分比
- Branches: % of if/else paths
- Branches: if/else分支的执行百分比
- Functions: % of functions called
- Functions: 已调用函数的百分比
- Lines: % of lines executed
- Lines: 已执行代码行的百分比
undefinedundefinedTesting Best Practices
测试最佳实践
- ✅ AAA pattern: Arrange, Act, Assert
- ✅ One assertion per test (ideally)
- ✅ Descriptive test names
- ✅ Test edge cases
- ✅ Mock external dependencies
- ✅ Clean up after tests
- ✅ Avoid test interdependence
- ✅ Aim for 80%+ coverage
- ✅ AAA模式:准备(Arrange)、执行(Act)、断言(Assert)
- ✅ 每个测试用例尽量只包含一个断言
- ✅ 测试用例名称具有描述性
- ✅ 测试边界情况
- ✅ Mock外部依赖
- ✅ 测试后清理资源
- ✅ 避免测试用例之间的依赖
- ✅ 目标覆盖率达到80%以上
CI/CD Integration
CI/CD集成
yaml
undefinedyaml
undefined.github/workflows/test.yml
.github/workflows/test.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v3undefinedname: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm ci
- run: npm test -- --coverage
- uses: codecov/codecov-action@v3undefinedWhen to Use
适用场景
Use Jest testing when:
- Building Node.js applications
- Need comprehensive test coverage
- Want fast, parallel test execution
- Require mocking and snapshot testing
- Implementing CI/CD pipelines
在以下场景使用Jest测试:
- 开发Node.js应用
- 需要全面的测试覆盖率
- 希望快速、并行执行测试
- 需要Mock和快照测试
- 实现CI/CD流水线
Related Skills
相关技能
- Express REST API (test API endpoints)
- Async Programming (test async code)
- Database Integration (test DB operations)
- JWT Authentication (test auth flows)
- Express REST API(测试API端点)
- 异步编程(测试异步代码)
- 数据库集成(测试数据库操作)
- JWT认证(测试认证流程)