使用Sinon stub模拟dns lookup时无法触发对应回调问题排查
问题原因
- 你对回调风格API的桩实现逻辑错误:
dns.lookup是回调模式的原生接口,你使用stub().resolves()是为Promise风格接口打桩的写法,这种方式只会返回一个成功态的Promise,根本不会执行你传入的第三个参数也就是回调函数,所以回调永远不会被触发。 - 打桩的时机和操作对象错误:你在
beforeEach里对collectDns做__set__依赖注入,但你是在it用例内部才通过rewire引入的collectDns模块,beforeEach里操作的collectDns不是你实际测试用的实例,打桩根本没有生效。 - 没有处理异步测试的等待逻辑:
collectDns虽然标记了async,但内部没有返回Promise也没有await逻辑,你在测试里直接调用collectDns()后用例会直接执行结束,就算回调能触发,你也等不到执行时机。 - 参数匹配逻辑存在问题:你调用
lookup时传了all: true,原生接口此时返回的地址是数组格式,你当前的fake数据是单个对象,就算回调能触发,后续逻辑也会因为数据格式不对出错。
修正方案
1. 调整业务代码(可选,建议适配async语法)
如果坚持使用回调风格,需要将逻辑包装为Promise返回,适配异步测试:
const { lookup } = require('dns'); module.exports = async function collectDns() { return new Promise((resolve, reject) => { lookup('google.com', { all: true, family: 4 }, (err, addresses) => { console.log(err); console.log(addresses); err ? reject(err) : resolve(addresses); }); }) };
也可以直接使用Node.js内置的Promise版本dns接口,代码更简洁:
const { lookup } = require('dns').promises; module.exports = async function collectDns() { try { const addresses = await lookup('google.com', { all: true, family: 4 }); console.log(addresses); return addresses; } catch (err) { console.log(err); throw err; } };
2. 修正单元测试代码
const { stub } = require('sinon'); const rewire = require('rewire'); const expect = require('chai').expect; describe('collectDns 测试', () => { let collectDns, revert, lookupStub; // 先引入模块再打桩 beforeEach('stub deps', () => { collectDns = rewire('../collectdns'); const lookupFake = [{ address: '127.0.0.1', family: 4 }]; // 匹配all:true的数组返回格式 // 回调风格接口打桩:调用时主动触发回调 lookupStub = stub().callsFake((domain, options, callback) => { callback(null, lookupFake); }); // 如果是使用Promise版本的lookup,上面的打桩代码替换为下面这行即可 // lookupStub = stub().resolves(lookupFake); revert = collectDns.__set__({ 'lookup': lookupStub, }); }); afterEach(() => { revert(); // 执行恢复逻辑,避免桩污染其他用例 }); it('run DNS lookup', async () => { const res = await collectDns(); // 等待异步逻辑执行完成 // 断言调用符合预期 expect(lookupStub.calledOnce).to.be.true; expect(lookupStub.calledWith('google.com', { all: true, family: 4 })).to.be.true; expect(res).to.deep.equal([{ address: '127.0.0.1', family: 4 }]); }); })
内容的提问来源于stack exchange,提问作者Bob
相关产品推荐
相关产品推荐

