引言:为什么高级测试是 JavaScript 项目的必备技能
在现代 Node.js 开发中,测试已不再是可选环节。如果没有高质量的代码测试覆盖率,任何重构或添加新功能都可能变成一场“俄罗斯轮盘赌”。然而,仅仅编写几个 assert 语句是不够的。在高级层面,开发者必须掌握三个主要层次:单元测试(验证独立模块)、集成测试(组件、数据库、API 之间的交互)和端到端测试(模拟用户操作)。在本文中,我们将以 Node.js 为例,结合 Jest、Supertest 和 Playwright 等现代工具,深入剖析每个层次的实用技巧。
1. 单元测试:高级隔离、模拟与桩代码
单元测试用于验证单个函数或模块在隔离外部依赖(如数据库、文件系统、网络请求)时的运行情况。关键任务是正确地模拟这些依赖,以确保测试快速且结果确定。
Jest 中的高级模拟技术
考虑一个示例:一个函数会调用外部 API 并将结果缓存到 Redis。我们需要在不执行真实请求的情况下测试缓存逻辑。
// cacheService.jsconst axios = require('axios');const redis = require('redis');
class CacheService { constructor() { this.client = redis.createClient(); }
async fetchData(url) { const cached = await this.client.get(url); if (cached) return JSON.parse(cached);
const response = await axios.get(url); await this.client.setEx(url, 3600, JSON.stringify(response.data)); return response.data; }}
module.exports = CacheService;// cacheService.test.jsconst CacheService = require('./cacheService');const axios = require('axios');const redis = require('redis');
jest.mock('axios');jest.mock('redis', () => ({ createClient: jest.fn(() => ({ get: jest.fn(), setEx: jest.fn() }))}));
describe('CacheService', () => { let service; let mockRedisClient;
beforeEach(() => { mockRedisClient = redis.createClient(); service = new CacheService(); });
test('should return cached data if present', async () => { const url = 'https://api.example.com/data'; const cachedData = { id: 1, value: 'cached' }; mockRedisClient.get.mockResolvedValue(JSON.stringify(cachedData));
const result = await service.fetchData(url);
expect(result).toEqual(cachedData); expect(axios.get).not.toHaveBeenCalled(); });
test('should fetch and cache data if not cached', async () => { const url = 'https://api.example.com/data'; const apiData = { id: 2, value: 'fresh' }; mockRedisClient.get.mockResolvedValue(null); axios.get.mockResolvedValue({ data: apiData });
const result = await service.fetchData(url);
expect(result).toEqual(apiData); expect(mockRedisClient.setEx).toHaveBeenCalledWith(url, 3600, JSON.stringify(apiData)); });});关键要点:
jest.mock('axios')会自动模拟整个 axios 模块。- 对于 Redis,我们使用一个工厂函数,返回一个包含
get和setEx方法的模拟对象。 - 我们验证了当缓存存在时,外部调用不会发生。
2. 集成测试:测试与真实数据库和 API 的交互
集成测试用于验证系统不同部分如何协同工作。对于 Node.js,这通常意味着使用真实(或测试)数据库来测试 Express 路由。最佳工具是 Supertest 与 Jest 配合使用。
配置测试数据库和 Supertest
示例:一个用于用户管理的 API。我们使用内存中的 SQLite 来实现隔离