Jest项目实战:新手入门指南
本文将详细介绍如何在项目中使用Jest进行测试,包括Jest的安装、配置以及编写测试用例的方法,帮助新手快速掌握Jest项目实战。
Jest项目实战:新手入门指南 Jest简介与安装什么是Jest
Jest是Facebook开源的一个JavaScript测试框架,主要用于测试JavaScript、TypeScript、React等Web应用。Jest具有零配置、快速运行、内置Mock函数等功能,使得编写和执行单元测试变得更加简单。Jest包含以下特性:
- 自动收集测试
- 断言库
- 内置Mock函数
- 代码覆盖率报告
- 模拟DOM和浏览器对象
- 测试环境快照
- 自动创建测试文件
- 命令行工具集成
- Jest还内置了对React的特殊支持
安装Jest
Jest可以通过npm进行安装。在命令行中运行以下命令来安装Jest:
npm install --save-dev jest
安装完成后,Jest将作为项目的依赖项,在项目中使用其提供的各种测试工具。
配置Jest
默认情况下,Jest已经提供了基本配置。但如果需要自定义配置,可以创建一个jest.config.js
文件,并在其中添加配置选项。例如,添加以下配置选项:
module.exports = {
// 指定测试文件的扩展名
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(js|jsx|ts|tsx)$',
// 指定模块解析的根目录
moduleFileExtensions: [
'js',
'jsx',
'ts',
'tsx',
],
// 指定测试文件的根目录
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
// 指定是否使用ES模块
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|svg|mp4|mov|mpeg)$': '<rootDir>/__mocks__/fileMock.js',
'\\.(css|less)$': '<rootDir>/__mocks__/styleMock.js',
},
// 指定测试时使用的端口号
testEnvironment: 'jsdom',
};
这些配置选项可以根据项目需求进行调整,包括指定测试文件的扩展名、测试文件的根目录以及模块解析的根目录等。
编写第一个测试用例测试函数
测试函数是测试中最基本的部分。编写测试函数需要选择一个断言库,并使用它提供的断言方法来验证函数的输出是否符合预期。例如:
// 被测函数
function add(a, b) {
return a + b;
}
// 测试用例
describe('add', () => {
it('should return the sum of two numbers', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
});
});
在示例代码中,describe
函数用于定义测试套件,it
函数用于定义具体的测试用例。expect
函数用于断言实际结果是否符合预期结果。
测试异步函数
测试异步函数时,可以使用Promise
或async/await
语法。Jest提供了async
修饰符来简化异步测试的编写。例如:
// 被测异步函数
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// 测试用例
describe('delay', () => {
it('should resolve after the given delay', async () => {
const start = Date.now();
await delay(100);
const end = Date.now();
expect(end - start).toBeGreaterThan(100);
});
});
在示例代码中,delay
函数返回一个Promise
,在it
函数中使用await
关键字等待Promise
解析完成。通过比较开始时间和结束时间,确保delay
函数运行了指定的时间。
测试类与方法
测试类和方法时,可以使用class
来定义被测对象,并使用describe
和it
来定义测试用例。可以通过实例化对象来调用类中的方法,并使用expect
函数进行断言。例如:
// 被测类
class Calculator {
add(a, b) {
return a + b;
}
subtract(a, b) {
return a - b;
}
}
// 测试用例
describe('Calculator', () => {
it('should add two numbers', () => {
const calculator = new Calculator();
expect(calculator.add(1, 2)).toBe(3);
});
it('should subtract two numbers', () => {
const calculator = new Calculator();
expect(calculator.subtract(2, 1)).toBe(1);
});
});
在示例代码中,定义了一个Calculator
类,并在describe
函数中定义了测试用例。通过实例化Calculator
类并调用其方法,验证结果是否符合预期。
describe、it、expect
describe
函数用于定义测试套件,通常包含一组相关的测试用例。it
函数用于定义具体的测试用例,每个测试用例负责验证一个特定的功能。expect
函数用于断言实际结果是否符合预期结果。例如:
describe('Calculator', () => {
it('should add two numbers', () => {
const calculator = new Calculator();
expect(calculator.add(1, 2)).toBe(3);
});
it('should subtract two numbers', () => {
const calculator = new Calculator();
expect(calculator.subtract(2, 1)).toBe(1);
});
});
在示例代码中,定义了一个Calculator
类的测试套件,并使用describe
和it
函数编写了两个测试用例。通过实例化Calculator
类并调用其方法,使用expect
断言验证结果是否符合预期。
Mock函数与Spy函数
Mock函数和Spy函数是Jest提供的用于模拟和监视函数行为的工具。Mock函数用于模拟函数的行为,Spy函数用于监视函数调用。例如:
// 被测函数
function fetchData(url) {
return fetch(url);
}
// Mock函数
jest.mock('fetch', () => {
return jest.fn().mockResolvedValue({ data: 'mocked data' });
});
// 测试用例
describe('fetchData', () => {
it('should return mocked data', async () => {
const result = await fetchData('https://api.example.com');
expect(result.data).toBe('mocked data');
});
});
在示例代码中,fetchData
函数使用了fetch
函数。通过jest.mock
函数模拟了fetch
函数的行为,并返回一个预设的Promise
。在测试用例中,调用fetchData
函数并验证返回的结果是否符合预期。
测试覆盖率
代码覆盖率是指测试代码对源代码的覆盖率。Jest提供了内置的代码覆盖率报告功能,可以通过配置选项启用代码覆盖率统计,并在测试运行时生成覆盖率报告。例如:
// 配置文件jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: ['\\\\node_modules\\\\'],
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
};
在示例代码中,通过配置选项collectCoverage
启用代码覆盖率统计,并设置覆盖率报告的输出路径。coveragePathIgnorePatterns
用于排除某些文件或目录的覆盖率统计。
测试代码组织
测试代码组织的原则是将测试用例和代码分开,保持清晰的结构。通常将测试文件放在与代码文件相同的目录下,但放在__tests__
子目录中。例如:
src/
└── utils/
├── add.js
└── __tests__
└── add.test.js
在示例代码中,add.js
是实际的代码文件,add.test.js
是对应的测试文件。将测试文件放在__tests__
子目录中,可以清晰地将测试文件和代码文件分开。
测试代码风格
测试代码风格的目标是保持一致性和可读性。建议遵循以下几点:
- 使用
describe
和it
函数组织测试用例。 - 使用
expect
函数进行断言。 - 使用合适的描述语句,使测试用例的意图清晰明了。
示例代码:
describe('add', () => {
it('should return the sum of two numbers', () => {
expect(add(1, 2)).toBe(3);
});
it('should return zero when adding negative and positive numbers', () => {
expect(add(-1, 1)).toBe(0);
});
});
在示例代码中,使用了describe
和it
函数组织测试用例,并使用了合适的描述语句,使测试用例的意图清晰明了。
依赖注入与Mock
依赖注入和Mock是提高测试代码可维护性和可读性的重要手段。通常在编写测试用例时,将依赖项作为参数传递给被测函数,而不是直接引入依赖项。例如:
// 描述类
class ServiceClient {
fetch(url) {
return fetch(url);
}
}
// 被测函数
function fetchData(url, client) {
return client.fetch(url);
}
// 测试用例
describe('fetchData', () => {
it('should return mocked data', async () => {
const mockFetch = jest.fn().mockResolvedValue({ data: 'mocked data' });
const mockClient = new ServiceClient();
mockClient.fetch = mockFetch;
const result = await fetchData('https://api.example.com', mockClient);
expect(result.data).toBe('mocked data');
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com');
});
});
在示例代码中,fetchData
函数接收一个fetch
函数作为参数。在测试用例中,通过传递一个模拟的fetch
函数来测试fetchData
函数的行为。这种方式可以避免直接引入依赖项,使测试用例更加灵活和可维护。
在持续集成环境中运行Jest
在持续集成环境中运行Jest,可以确保每次代码提交后自动运行测试用例,确保代码质量。通常在持续集成服务(如GitHub Actions、GitLab CI等)中配置Jest的执行任务。例如:
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
在示例代码中,配置了一个GitHub Actions的工作流,定义了test
作业,运行在ubuntu-latest
环境中。在作业中,执行了npm ci
和npm test
命令,以安装依赖项和运行测试用例。
运行测试并收集覆盖率数据
在持续集成环境中运行Jest时,可以通过配置选项启用代码覆盖率统计,并收集覆盖率数据。例如:
// 配置文件jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coveragePathIgnorePatterns: ['\\\\node_modules\\\\'],
testPathIgnorePatterns: ['\\\\node_modules\\\\'],
};
在示例代码中,通过配置选项collectCoverage
启用代码覆盖率统计,并设置覆盖率报告的输出路径。coveragePathIgnorePatterns
用于排除某些文件或目录的覆盖率统计。
集成Jest的测试报告
集成Jest的测试报告可以帮助持续集成服务更好地展示测试结果。通常在持续集成服务中配置任务,将测试报告上传到指定的位置或服务。例如:
name: Test
on: [push]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Upload coverage report
uses: coverallsapp/github-action@v1.6.5
with:
path_to_coverages: coverage
在示例代码中,定义了一个GitHub Actions的工作流,运行在ubuntu-latest
环境中。在作业中,执行了npm ci
和npm test
命令,以安装依赖项和运行测试用例。最后,使用coverallsapp/github-action
任务将覆盖率报告上传到Coveralls服务。
测试覆盖率低的原因与解决方法
测试覆盖率低的原因可能包括:
- 测试覆盖不全面,未覆盖所有代码分支和路径。
- 测试用例简单,缺乏复杂性和边缘情况的测试。
- 代码中存在未测试的部分,例如复杂的业务逻辑或异常处理。
解决方法包括:
- 逐步增加测试覆盖,确保所有代码分支都被覆盖。
- 编写更多的测试用例,包括复杂和边缘情况。
- 重构代码,使其更容易测试。
示例代码:
// 被测函数
function add(a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new Error('Both arguments must be numbers');
}
return a + b;
}
// 测试用例
describe('add', () => {
it('should throw error when arguments are not numbers', () => {
expect(() => add('1', 2)).toThrow('Both arguments must be numbers');
expect(() => add(1, '2')).toThrow('Both arguments must be numbers');
});
it('should return the sum of two numbers', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
});
});
在示例代码中,定义了一个抛出错误的add
函数。在测试用例中,编写了多个测试用例,包括抛出错误的情况和正常情况。
Mock函数的常见误区
Mock函数的常见误区包括:
- 过度依赖Mock函数,导致代码难以理解和维护。
- Mock函数的实现过于复杂,难以理解和调试。
- Mock函数和真实函数的实现不一致,导致测试结果不一致。
解决方法包括:
- 仅在必要时使用Mock函数,尽量使用真实的依赖项。
- Mock函数的实现应尽可能简单,只模拟必要的行为。
- Mock函数和真实函数的实现应保持一致,确保测试结果的一致性。
示例代码:
// 被测函数
function fetchData(url) {
return fetch(url);
}
// Mock函数
jest.mock('fetch', () => {
return jest.fn().mockResolvedValue({ data: 'mocked data' });
});
// 测试用例
describe('fetchData', () => {
it('should return mocked data', async () => {
const result = await fetchData('https://api.example.com');
expect(result.data).toBe('mocked data');
expect(fetch).toHaveBeenCalledWith('https://api.example.com');
});
});
在示例代码中,定义了一个使用fetch
函数的fetchData
函数。在测试用例中,使用Mock函数模拟了fetch
函数的行为,并验证了返回的结果和函数调用。
Jest与TypeScript配合使用
Jest与TypeScript配合使用时,需要注意以下几点:
- TypeScript需要安装并配置,确保项目支持TypeScript。
- Jest需要安装TypeScript相关的依赖项。
- 配置文件需要包含TypeScript相关的选项。
示例代码:
npm install --save-dev jest ts-jest @types/jest
在示例代码中,安装了Jest和ts-jest
依赖项,并安装了@types/jest
类型定义。
示例配置文件jest.config.js
:
module.exports = {
preset: 'ts-jest',
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(ts|tsx|js|jsx)$',
moduleFileExtensions: [
'ts',
'tsx',
'js',
'jsx',
'json',
'node',
],
};
在示例配置文件中,使用了ts-jest
预设,并配置了测试文件的扩展名和模块解析的根目录。
示例代码:
// 被测函数
function add(a: number, b: number): number {
return a + b;
}
// 测试用例
describe('add', () => {
it('should return the sum of two numbers', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
});
});
在示例代码中,定义了一个返回数字类型结果的add
函数。在测试用例中,使用expect
断言验证函数的结果是否符合预期。
共同学习,写下你的评论
评论加载中...
作者其他优质文章