如何在Jasmine/Jest中对唯一方法调用进行单元测试?
实现方案
你需要先把被测的foo函数替换为Jasmine/Jest的Mock函数,之后通过统计指定参数的匹配调用次数来实现断言,不需要依赖原生的toHaveBeenCalledOnceWith匹配器。
基础通用实现(兼容Jasmine和Jest)
直接过滤Mock的调用记录,匹配目标参数后断言次数为1即可:
// 示例:断言 foo 传入参数 (1, "test", {id: 2}) 的调用仅出现1次 const targetArgs = [1, "test", {id: 2}]; // 筛选出参数匹配的调用次数 // Jasmine 环境写法 const matchCount = foo.calls.allArgs().filter(args => jasmine.matchersUtil.equals(args, targetArgs)).length; // Jest 环境写法,替换上面一行即可 // const matchCount = foo.mock.calls.filter(args => expect.equals(args, targetArgs)).length; expect(matchCount).toBe(1);
上述写法直接用框架内置的深比较逻辑,支持识别expect.any(Number)这类非对称匹配器参数,不需要自己写参数判断逻辑。
进阶:封装为自定义匹配器
如果需要多次使用该逻辑,可以封装为自定义匹配器简化调用,以Jest为例:
// 放在jest.setup.js中全局注册,或单测文件顶部局部注册 expect.extend({ toHaveBeenCalledNTimesWith(mockFn, expectedTimes, ...expectedArgs) { if (!mockFn.mock) { return { pass: false, message: () => `传入的参数不是合法的Mock函数` } } const matchedCount = mockFn.mock.calls.filter(call => this.equals(call, expectedArgs)).length; const pass = matchedCount === expectedTimes; return { pass, message: () => pass ? `预期${mockFn.getMockName()}调用${expectedArgs}的次数不等于${expectedTimes},实际为${matchedCount}` : `预期${mockFn.getMockName()}调用${expectedArgs}的次数为${expectedTimes},实际为${matchedCount}` } } }) // 测试中直接调用即可 // 断言 foo 调用参数为 1, "test" 的次数为1 expect(foo).toHaveBeenCalledNTimesWith(1, 1, "test"); // 也可以复用匹配其他次数,比如断言参数为 2 的调用出现过2次 expect(foo).toHaveBeenCalledNTimesWith(2, 2);
内容的提问来源于stack exchange,提问作者danchez
相关产品推荐
相关产品推荐

