gpt4 book ai didi

javascript - Sinon stub 定义在同一文件中的函数

转载 作者:行者123 更新时间:2023-12-05 05:48:05 28 4
gpt4 key购买 nike

我的代码如下:

// example.js
export function doSomething() {
if (!testForConditionA()) {
return;
}

performATask();
}

export function testForConditionA() {
// tests for something and returns true/false
// let's say this function hits a service or a database and can't be run in tests
...
}

export function performATask() {
...
}


// example.test.js
import * as example from 'example';

it('validates performATask() runs when testForConditionA() is true', () => {
const testForConditionAStub = sinon.stub(example, 'testForConditionA').returns(true);
const performATaskSpy = sinon.stub(example, 'performATask');

example.doSomething();
expect(performATaskSpy.called).to.equal(true);
});

(我知道,这是一个人为的例子,但我尽量保持简短)

我还没有找到使用 Sinon 模拟 testForConditionA() 的方法。

我知道有变通办法,比如

A) 将 example.js 中的所有内容放入一个类中,然后可以 stub 该类的函数。

B) 将 testForConditionA() (和其他依赖项)从 example.js 移到一个新文件中,然后使用 proxyquire

C) 将依赖项注入(inject) doSomething()

但是,这些选项都不可行 - 我在大型代码库中工作,许多文件需要重写和大修。我已经搜索过这个主题,并且看到了其他几篇文章,例如 Stubbing method in same file using Sinon ,但是除了将代码重构为一个单独的类(或有人建议的工厂),或者重构为一个单独的文件并使用 proxyquire 之外,我还没有找到解决方案。我过去曾使用过其他测试和模拟库,所以令人惊讶的是 Sinon 无法做到这一点。或者是吗?关于如何在不重构它试图测试的代码的情况下对函数进行 stub 的任何建议?

最佳答案

此位来自 a very related answer (我的),说明了为什么它并不那么令人惊讶:

ES modules are not mutable by default, which means Sinon can't do zilch.

EcmaScript 规范规定了这一点,因此当前改变导出的唯一方法是让运行时不遵守规范。这基本上就是 Jest 所做的:它提供自己的运行时,将导入调用转换为等效的 CJS 调用 (require) 调用,并在该运行时提供自己的 require 实现以 Hook 进入加载过程。生成的“模块”通常具有您可以覆盖的可变导出(即 stub )。

Jest 也不支持原生(因为没有源代码的转译/修改)ESM。跟踪问题 48429430这有多复杂(需要更改 Node)。

所以,不,诗乃自己是做不到的。它只是一个 stub 库。它不会触及运行时或做任何神奇的事情,因为它必须在任何环境下都能正常工作。


现在回到您最初的问题:测试您的模块。我看到这种情况发生的唯一方法是通过某种依赖注入(inject)机制(您在备选方案 C 中提到过)。您显然有一些(内部/外部)状态是您的模块所依赖的,因此这意味着您需要一种从外部更改该状态或注入(inject)测试替身(您正在尝试的)的方法。

一个简单的方法就是创建一个严格用于测试的 setter:

function callNetworkService(...args){
// do something slow or brittle
}

let _doTestForConditionA = callNetworkService;

export function __setDoTestForConditionA(fn){
_doTestForConditionA = fn;
}

export function __reset(){
_doTestForConditionA = callNetworkService;
}

export function testForConditionA(...args) {
return _doTestForConditionA(...args);
}

然后您可以像这样简单地测试您的模块:

afterEach(() => {
example.__reset();
});

test('that my module calls the outside and return X', async () => {
const fake = sinon.fake.resolves({result: 42});
example.__setDoTestForConditionA(fake);
const pendingPromise = example.doSomething();
expect(fake.called).to.equal(true);

expect((await pendingPromise).result).toEqual(42);
});

是的,您确实修改了您的 SUT 以允许测试,但我从未发现这一切令人反感。无论框架(Jasmine、Mocha、Jest)或运行时(浏览器、Node、JVM)如何,该技术都适用,并且读起来很好。

选择性地注入(inject)依赖

您确实提到将依赖项注入(inject)到实际上依赖于它们的函数中,这会产生一些会传播到整个代码库的问题。

我想通过展示我过去使用过的技术来挑战一下。请参阅我在 Sinon 问题跟踪器上的评论:https://github.com/sinonjs/sinon/issues/831#issuecomment-198081263

我使用这个示例来展示如何在构造函数中注入(inject) stub ,而该构造函数的普通使用者无需关心这些 stub 。当然,确实需要您使用某种Object 来不添加其他参数。

/**
* Request proxy to intercept and cache outgoing http requests
*
* @param {Number} opts.maxAgeInSeconds how long a cached response should be valid before being refreshed
* @param {Number} opts.maxStaleInSeconds how long we are willing to use a stale cache in case of failing service requests
* @param {boolean} opts.useInMemCache default is false
* @param {Object} opts.stubs for dependency injection in unit tests
* @constructor
*/
function RequestCacher (opts) {
opts = opts || {};
this.maxAge = opts.maxAgeInSeconds || 60 * 60;
this.maxStale = opts.maxStaleInSeconds || 0;
this.useInMemCache = !!opts.useInMemCache;
this.useBasicToken = !!opts.useBasicToken;
this.useBearerToken = !!opts.useBearerToken;

if (!opts.stubs) {
opts.stubs = {};
}

this._redisCache = opts.stubs.redisCache || require('./redis-cache');
this._externalRequest = opts.stubs.externalRequest || require('../request-helpers/external-request-handler');
this._memCache = opts.stubs.memCache || SimpleMemCache.getSharedInstance();
}

(请参阅问题跟踪器以获取更多评论)

没有什么可以强制任何人提供 stub ,但是测试可以提供它们来覆盖依赖项的工作方式。

关于javascript - Sinon stub 定义在同一文件中的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70873822/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com