如何Stub node-slack-sdk实例中chat嵌套方法以完成Express集成测试?
如何Stub Slack SDK中chat下的嵌套方法进行集成测试
刚好我之前在Express项目里做过类似的Slack集成测试,核心就是把chat.postMessage这类嵌套方法Stub掉,不让它真发送消息,只验证调用情况。下面分两种常用测试工具(Jest和Sinon)来讲具体实现:
情况1:复用Slack Client单例实例
如果你的代码里提前初始化好WebClient实例并导出复用(比如单独写个slackClient.js),Stub起来很简单:
用Jest实现
// 测试文件:routes.test.js const request = require('supertest'); const app = require('../app'); // 导入项目里的Slack Client单例 const slackClient = require('../slackClient'); // 测试前Stub chat.postMessage方法 jest.spyOn(slackClient.chat, 'postMessage').mockResolvedValue({ ok: true }); describe('POST /send-slack-message', () => { it('应该用正确参数调用Slack的postMessage方法', async () => { // 发送请求到目标路由 await request(app) .post('/send-slack-message') .send({ text: '测试消息', channel: '#测试频道' }); // 断言方法调用次数和参数 expect(slackClient.chat.postMessage).toHaveBeenCalledTimes(1); expect(slackClient.chat.postMessage).toHaveBeenCalledWith({ channel: '#测试频道', text: '测试消息' }); }); // 每个测试结束后恢复原方法,避免影响其他测试 afterEach(() => { slackClient.chat.postMessage.mockRestore(); }); });
用Sinon实现
// 测试文件:routes.test.js const request = require('supertest'); const app = require('../app'); const slackClient = require('../slackClient'); const sinon = require('sinon'); describe('POST /send-slack-message', () => { let postMessageStub; beforeEach(() => { // 创建Stub,模拟异步返回成功结果 postMessageStub = sinon.stub(slackClient.chat, 'postMessage').resolves({ ok: true }); }); it('应该用正确参数调用Slack的postMessage方法', async () => { await request(app) .post('/send-slack-message') .send({ text: '测试消息', channel: '#测试频道' }); // Sinon的断言方式 sinon.assert.calledOnce(postMessageStub); sinon.assert.calledWith(postMessageStub, { channel: '#测试频道', text: '测试消息' }); }); afterEach(() => { // 恢复原方法 postMessageStub.restore(); }); });
情况2:每次请求新建WebClient实例
如果路由代码里每次请求都new一个WebClient,需要Mock整个WebClient类,而不是单个实例:
用Jest实现
// 测试文件:routes.test.js const request = require('supertest'); const app = require('../app'); // 导入WebClient类 const { WebClient } = require('@slack/web-api'); // Mock整个@slack/web-api模块 jest.mock('@slack/web-api'); describe('POST /send-slack-message', () => { it('应该用正确参数调用Slack的postMessage方法', async () => { // 定义mock的postMessage方法 const mockPostMessage = jest.fn().mockResolvedValue({ ok: true }); // 让WebClient实例返回包含mock方法的chat对象 WebClient.mockImplementation(() => ({ chat: { postMessage: mockPostMessage } })); await request(app) .post('/send-slack-message') .send({ text: '测试消息', channel: '#测试频道' }); expect(mockPostMessage).toHaveBeenCalledTimes(1); expect(mockPostMessage).toHaveBeenCalledWith({ channel: '#测试频道', text: '测试消息' }); }); });
用Sinon实现
// 测试文件:routes.test.js const request = require('supertest'); const app = require('../app'); const { WebClient } = require('@slack/web-api'); const sinon = require('sinon'); describe('POST /send-slack-message', () => { let webClientStub; let postMessageStub; beforeEach(() => { // Stub postMessage方法 postMessageStub = sinon.stub().resolves({ ok: true }); // Stub WebClient构造函数,返回包含mock方法的实例 webClientStub = sinon.stub(WebClient, 'default').returns({ chat: { postMessage: postMessageStub } }); }); it('应该用正确参数调用Slack的postMessage方法', async () => { await request(app) .post('/send-slack-message') .send({ text: '测试消息', channel: '#测试频道' }); sinon.assert.calledOnce(postMessageStub); sinon.assert.calledWith(postMessageStub, { channel: '#测试频道', text: '测试消息' }); }); afterEach(() => { // 恢复原构造函数和方法 webClientStub.restore(); postMessageStub.restore(); }); });
关键注意点
- Stub要精准:确保Stub的是代码实际调用的方法——是单例实例的方法,还是每次新建实例的构造函数返回的方法,别Stub错对象。
- 模拟异步返回:Slack方法是异步的,一定要用
mockResolvedValue(Jest)或resolves(Sinon)模拟成功/失败返回,否则测试会因异步等待超时失败。 - 清理Stub:每个测试结束后恢复原方法,防止Stub污染其他测试用例的环境。
内容的提问来源于stack exchange,提问作者bookcasey
相关产品推荐
相关产品推荐

