如何在Express API中使用Jest Mock路由内调用的DynamoDB
解决思路
核心原理是利用Jest的模块预Mock能力,在导入Express App实例前就替换掉aws-sdk的导出内容,这样路由文件中初始化的DynamoDB.DocumentClient实例默认就是你Mock的版本,路由执行时调用的DynamoDB方法都会走你定义的Mock逻辑。
具体实现步骤
- 固定nanoid的返回值,避免随机生成的URL ID影响测试断言
- Mock整个aws-sdk模块,替换DynamoDB.DocumentClient的构造逻辑
- 给Mock的DocumentClient实例的
get、transactWrite方法绑定自定义实现 - 根据测试场景覆盖不同的返回分支(无历史链接/有历史链接/接口报错等)
修改后的test.js完整代码
const request = require('supertest'); // 提前Mock依赖模块,Jest会把这段Mock逻辑提升到所有模块导入之前执行 jest.mock('aws-sdk'); jest.mock('nanoid/async', () => ({ nanoid: jest.fn(() => Promise.resolve('testid88')) // 固定返回8位测试ID })); const AWS = require('aws-sdk'); const app = require('../app'); // 每次测试前重置Mock实例状态 beforeEach(() => { AWS.DynamoDB.DocumentClient.mockClear(); }); test('新的长链接应成功生成短链接并返回201', async () => { // 获取路由文件中new出来的Mock DocumentClient实例 const mockDynamoClient = AWS.DynamoDB.DocumentClient.mock.instances[0]; // Mock get方法的实现:模拟当前长链接没有历史存储记录 mockDynamoClient.get.mockImplementation((params, callback) => { callback(null, {}); // 第二个参数返回空对象,代表无匹配记录 }); // Mock transactWrite方法的实现:模拟写入成功 mockDynamoClient.transactWrite.mockImplementation((params, callback) => { callback(null); }); const res = await request(app) .post('/anon-ops/convert') .send({ longUrl: 'https://google.com', }) .expect(201); // 可加额外断言验证返回值 expect(res.body.convertedUrl).toBe('https://xxxxxxxxxxxx/testid88'); // 验证Dynamo方法被正确调用 expect(mockDynamoClient.get).toHaveBeenCalledTimes(1); expect(mockDynamoClient.transactWrite).toHaveBeenCalledTimes(1); }); test('已存在的长链接应直接返回历史短链接', async () => { const mockDynamoClient = AWS.DynamoDB.DocumentClient.mock.instances[0]; // Mock get方法返回已有存储记录 mockDynamoClient.get.mockImplementation((params, callback) => { callback(null, { Item: { userId: 'anonymous', originalUrl: 'https://google.com', convertedUrl: 'https://xxxxxxxxxxxx/existid1' } }); }); const res = await request(app) .post('/anon-ops/convert') .send({ longUrl: 'https://google.com', }) .expect(201); expect(res.body.convertedUrl).toBe('https://xxxxxxxxxxxx/existid1'); // 已有记录的场景下不会调用transactWrite expect(mockDynamoClient.transactWrite).not.toHaveBeenCalled(); });
注意事项
- Jest的
jest.mock会被自动提升到文件顶部执行,所以只要写在测试文件里,不管位置在哪,都会在导入app之前完成对aws-sdk的替换 - 如果要测试报错场景,只需要在Mock方法的callback第一个参数传入Error对象即可,比如
callback(new Error('Dynamo error'))就能验证500返回的逻辑 - 每次测试完成后清空Mock调用记录,避免不同用例之间的状态干扰
内容的提问来源于stack exchange,提问作者Jatin Mehrotra
相关产品推荐
相关产品推荐

