Sinon桩不替换函数

19

我尝试使用一个虚拟模块并对其进行桩测试,但是它没有起作用。

app.js

function foo()
{
    return run_func()
}
function run_func()
{
    return '1'
}
exports._test = {foo: foo, run_func: run_func}

测试.js

app = require("./app.js")._test
describe('test', function(){
    it('test', function(){

        var test_stub = sinon.stub(app, 'run_func').callsFake(
          function(){
            return '0'
        })
        test_stub.restore()

        var res = app.foo()
        assert.equal('0', res)
    })
})

我尝试了这个建议: sinon stub not replacing function

但是结果还是一样。它没有替换这个函数。

1个回答

30

你有几个问题。首先是在创建存根后立即调用test_stub.restore(),这会导致它将自身替换为原始函数,从而完全撤销存根。

restore用于在测试完成后清理伪造的方法。因此,您确实需要调用它,但应在afterEach中调用。

您的第二个问题略微微妙。Sinon通过覆盖对象上函数的引用来工作,使其指向其他内容(在此情况下是存根)。它无法替换其他上下文中对同一函数的引用。

当您调用sinon.stub(app,'run_func')时,它有点像这样:

app.run_func = sinon.stub()

...但前者的方式会存储app.run_func的原始值和名称,这样您就可以轻松地恢复它。

请注意,在此时,变量app指向您使用exports._test = {foo: foo, run_func: run_func}导出的相同对象。然而,您的foo函数并没有通过该对象引用run_func。它直接在app.js的作用域中引用它,Sinon无法影响它。

请查看以下示例。您还会注意到我清理了其他一些内容:

app.js:

exports.foo = function() {
    return exports.run_func();
};

exports.run_func = function() {
    return '1';
};

test.js:

const app = require('./app');
const sinon = require('sinon');

describe('app', function() {
    describe('foo', function() {
        beforeEach(function() {
            sinon.stub(app, 'run_func').returns('0');
        });

        afterEach(function() {
            app.run_func.restore();
        });

        it('returns result of app.run_func', function() {
            assert.equal(app.foo(), '0');
        });
    });
});

请注意,在app.jsexports引用的对象与test.jsapp引用的对象完全相同。这是因为节点模块默认导出一个空对象,您可以通过exports变量进行赋值。


1
谢谢!我是JavaScript的新手,所以似乎exports._test = {foo: foo, run_func: run_func}创建了一个包含原始函数对象副本的新对象(我不知道这样说是否正确,我来自C ++),我认为它引用原始对象。我必须直接导出函数对象而不是副本吗? - J.R.
从 C/C++ 的角度来看,可以将 JavaScript 中的每个变量或对象属性视为指针。你并不是将函数复制到新对象中,而是给它引用,以便通过它访问函数。exports 是模块 require 默认返回的对象。默认情况下,它是一个空对象,你可以将函数引用分配给它以进行“导出”。这是一个相当不错的教程:https://www.sitepoint.com/understanding-module-exports-exports-node-js/ - sripberger
1
有没有一种方法可以替换函数,无论它被如何调用? - Ashok kumar
很遗憾,不行。这个限制是内置在JavaScript工作方式中的。在CommonJS模块中,该模块顶层的变量是局部的,不能从模块外部进行更改。因此,您需要通过一个对象来引用可替换的内容,该对象将在模块外部可访问。默认导出对象通常是最佳选择。 - sripberger
由于这个词在此页面上尚未找到:我们在这里看到的确实是一个闭包,一个引用(不是字面值而是函数),正如我们所知,函数是普通变量。它被牢固地“烘烤”在其中,因此无法移除。 - Frank N
我刚刚解决了这个关于ES6存根问题的SO帖子中提到的问题。链接 - Frank N

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接