在 Node.js 模块之外模拟构造函数(或其他函数)

4

我正在使用Jasmine编写我的测试,但我认为我会在任何其他测试框架中遇到这个问题。假设我们有一个名为foo的模块,其中包含两个函数BarBaz,它们是构造函数(但也可以只是普通函数):

var Bar = exports.Bar = function Bar() {
  this.baz = new Baz();
};

var Baz = exports.Baz = function Baz() {

};

现在我想测试Bar,但是带有一个假的Baz实现:
var foo = require('foo');

describe("foo.Bar", function() {
  it("initializes its own Baz", function() {
    spyOn(foo, 'Baz'); // this replaces foo.Baz with a fake implementation
    var bar = new foo.Bar();
    expect(foo.Baz).toHaveBeenCalled();
  });
});

问题在于这个测试会失败,因为Bar使用变量Baz实例化一个新的Baz,而该变量不能从外部更改。仅仅通过spyOn()交换的是exports.Baz

显然的解决方案是编写this.baz = new exports.Baz();,但这种方式有点笨拙。如果我有更多的函数想要在我的模块内使用,我将不得不总是使用exports.前缀调用它们。在这里是否有其他方法?


尝试使用 spyOn(window, 'Baz');expect(window.Baz).toHaveBeenCalled(); - Prusse
@Prusse,我猜Node没有window对象。 - Juliusz Gonera
抱歉,但如果有全局对象,您可以在其位置使用它。 - Prusse
@Prusse,恐怕我不明白。什么全局对象?难道不是拥有模块系统就意味着没有全局对象吗? - Juliusz Gonera
1个回答

1
如果你能够以某种方式解耦这两个类,比如允许将Baz类的其他实现提供给bar方法,那么我认为这是最好的方式,你应该选择它。

但是,如果你真的想把 exports.Baz 叫做 Baz,那我能想到一种方法,就是使用 with

据说,使用 with 通常是一种不好的习惯,应该避免在自己的代码中使用。但只要你知道自己在做什么,这也可以是一种合法的用法,甚至可以解决问题。

方法如下:

with(exports) {

    exports.Bar = function Bar() {
        console.log('this is bar!');
        this.baz = new Baz();
    };

    exports.Baz = function Baz() {
        console.log('original baz!');
    };

}

在另一个模块中,如果您将foo.Baz更改为其他内容,则foo内部的Baz也会查找它。
我仍然建议找到一种让这两个类彼此独立的方法,然后您只需给Bar想要的Baz实现即可。

使用with是有趣的,但我认为太过hackish了。我宁愿不将这两个“类”解耦,因为Baz从未在Bar以外实例化。你可能会说,我不应该测试Baz,因为它是一个内部实现细节,但实际上整个系统的行为非常复杂,我需要更精细的测试。无论如何,感谢你的建议。 - Juliusz Gonera

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