问题描述
在运行我的测试时,我有时想为我的测试提供 API。我希望在测试期间单独定义此 api,因此我想确保 sinon.restore()
删除此测试 api。这不会取代现有的 JS API。 (例如。不是像 window.requestAnimiationFrame
这样的东西)。假定此 API 全局存在。 (例如,在全局/窗口对象上)
现在如果我不关心删除这个API,测试完成后我会做以下事情:
globalAPIObject.someTestApi = sinon.fake.returns('something');
但是,sinon.restore
不会/不能在测试运行后删除 globalAPIObject.someTestApi。
我希望能够以与 stub
相同的方式使用 fake。 (但 sinon 不提供这个)
// !! this API doesn't exist !!
sinon.fake(globalAPIObject,'someTestApi').returns('something');
// !! this API doesn't exist !!
所以我改用存根:
// This doesn't work if globalAPIObject.someTestAPi doesn't already exist.
sinon.stub(globalAPIObject,'someTestApi').returns('something');
然而,这只适用于替换已经存在的道具/功能,所以我必须这样做:
globalAPIObject.someTestApi = () = {};
sinon.stub(globalAPIObject,'someTestApi').returns('something');
这不太理想。 (因为 globalAPIObject.someTestApi 在测试结束时没有被 sinon.restore() 删除。另外我宁愿只写一行)
因为我想提供一个不存在的 API 是很多人想做的事情,所以我想我遗漏了一些明显的东西。
以某种方式伪造/存根/模拟新 API 的最佳方法是什么,而不是 sinon.restore 之后删除它的所有痕迹?
解决方法
简而言之,您不能使用 sinon.restore()
执行此操作。我们明确地让 API 尝试替换不存在的道具,但我们在 sinon 团队中对此进行了更长时间的讨论,基本上came to the the conclusion that we should add something like sinon.define()
.仍然没有人制作该功能请求进入一个实际问题,所以如果我是你,我只会提交一个新问题作为你对 Sinon Github 跟踪器的功能请求。很多人都喜欢这个功能,而且实现起来并不难,所以只要让它可见:)
要使用当今现有的机器来实际执行此操作,我可能会按照评论中提到的方式执行此操作:在挂钩之前/之后使用您的测试框架来设置和拆除手动构建的存根。
如果我自己做,我可能会重做代码,以便能够将 API 注入您的模块,而不是依赖全局变量。这是另一种思想流派,因此,无论您的船如何:)
,鉴于 'oligofren' 有助于澄清 sinon.stub 不是我们的用例,但我们只是对其进行了扩展。
// sinonExtensions.js
import sinon from "sinon";
if (sinon.stub !== enhancedStub)
{
sinon.originalStub = sinon.stub;
sinon.stub = enhancedStub;
}
function enhancedStub(obj,method)
{
if (!obj)
return sinon.originalStub();
if (!obj[method])
obj[method] = () => { };
return sinon.originalStub(obj,method);
}
// TODO: enhance sinon.restore to return non-existing methods to undefined
我们只与其他 sinon 导入一起导入:
import sinon,{ mock } from "sinon";
import sinonChai from 'sinon-chai';
import 'sinonExtensions.js';
chai.use(sinonChai);
这对我们目前对 sinon.stub 的所有使用都有好处。
现在这个快乐的作品:
sinon.stub(globalAPIObject,'someTestApi').returns('something');