如何消除Jest e2e测试中重复创建账号与API Key的冗余代码
Jest + Supertest E2E测试冗余问题解决方案
方案1:单次创建API Key供全用例复用
Jest 同个describe块作用域内声明的变量,会在整个测试套件运行期间保存在内存中,配合beforeAll钩子(所有同块测试用例执行前仅运行1次),不需要额外持久化就能实现跨用例状态共享,这是E2E测试里最常用的全局初始化方案。
核心改动是在测试块顶层声明存储账号ID、API Key的共享变量,在beforeAll初始化应用后一次性完成账号和Key的创建,后续所有测试用例直接读取变量值即可,不用重复发创建请求。
核心实现代码:
import { Test, TestingModule } from '@nestjs/testing'; import { INestApplication } from '@nestjs/common'; import * as request from 'supertest'; import { AppModule } from '../src/app.module'; import { randBrand } from '@ngneat/falso'; describe('All e2e tests', () => { let app: INestApplication; // 声明跨用例共享变量 let sharedAccountId: string; let sharedApiKey: string; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); // 仅执行1次:创建测试账号和API Key const testAccountName = randBrand(); const accountRes = await request(app.getHttpServer()) .post('/accounts') .send({ name: testAccountName }) .expect(201); sharedAccountId = accountRes.body._id; const keyRes = await request(app.getHttpServer()) .post('/apikeys') .send({ accountId: sharedAccountId }) .expect(201); // 注意统一状态码,和接口实际返回保持一致 sharedApiKey = keyRes.body.apiKey; }); afterAll(async () => { await app.close(); }); describe('Users (e2e)', () => { it('patches an user (PATCH)', async () => { // 直接使用全局共享的API Key,无需重复创建 const res = await request(app.getHttpServer()) .patch('/users/1') .send({ name: 'SomeUser', properties: { age: '20' } }) .set('Accept', 'application/json') .set('x-api-Key', sharedApiKey) .expect(201); expect(res.body.accountId).toEqual(sharedAccountId); expect(res.body.name).toEqual('SomeUser'); }); }); describe('Events (e2e)', () => { it('creates an event (POST)', async () => { // 直接复用共享Key const res = await request(app.getHttpServer()) .post('/events') .send({ name: 'login', userId: '1', groupId: '2', properties: { client: 'mobile' }, }) .set('Accept', 'application/json') .set('x-api-Key', sharedApiKey) .expect(201); expect(res.body.accountId).toEqual(sharedAccountId); expect(res.body.name).toEqual('login'); }); }); });
注意:如果测试用例存在修改/删除API Key、改动账号状态的操作,会导致其他用例鉴权失败,这类场景下不要用全局共享Key,改用方案2。
方案2:抽离公共创建逻辑为可复用工具函数
如果测试场景需要每个用例使用独立的账号/Key(比如测试权限隔离、资源归属逻辑),可以把重复的创建逻辑抽成独立的异步工具函数,需要的时候直接调用即可,不用重复写嵌套请求逻辑。
第一步:定义工具函数
可以直接写在测试文件顶部,或者抽到单独的测试工具文件中:
/** * 创建测试账号及对应API Key * @param app Nest应用实例 * @returns 包含accountId和apiKey的对象 */ async function createTestAccountAndKey(app: INestApplication) { const accountName = randBrand(); const accountRes = await request(app.getHttpServer()) .post('/accounts') .send({ name: accountName }) .expect(201); const accountId = accountRes.body._id; const keyRes = await request(app.getHttpServer()) .post('/apikeys') .send({ accountId }) .expect(201); return { accountId, apiKey: keyRes.body.apiKey }; }
第二步:在用例中直接调用
所有需要凭证的用例,一行代码就能拿到独立的测试账号和Key:
describe('Events (e2e)', () => { it('creates an event (POST)', async () => { // 直接调用工具函数获取独立测试凭证 const { accountId, apiKey } = await createTestAccountAndKey(app); const res = await request(app.getHttpServer()) .post('/events') .send({ name: 'login', userId: '1', groupId: '2', properties: { client: 'mobile' }, }) .set('Accept', 'application/json') .set('x-api-Key', apiKey) .expect(201); expect(res.body.accountId).toEqual(accountId); expect(res.body.name).toEqual('login'); }); });
额外优化建议
- 优先使用
async/await语法代替多层.then()嵌套,代码可读性更高,也不容易出现回调层级错误 - 原代码中重复设置了两次
Accept请求头,保留一处即可 - 注意对齐接口实际返回的状态码,现有代码中创建API Key的断言混用了200和201,统一后可以避免不必要的测试报错
- 两种方案可以结合使用:普通无副作用的接口测试用全局共享Key提升测试速度,需要隔离性的测试场景用工具函数创建独立凭证
内容的提问来源于stack exchange,提问作者Luis Novo
相关产品推荐
相关产品推荐

