如何存根require()/ expect调用模块的"root"函数?

jbp*_*ros 8 javascript bdd stub node.js jasmine

请考虑以下茉莉花规格:

describe("something.act()", function() {
  it("calls some function of my module", function() {
    var mod = require('my_module');
    spyOn(mod, "someFunction");
    something.act();
    expect(mod.someFunction).toHaveBeenCalled();
  });
});
Run Code Online (Sandbox Code Playgroud)

这工作得非常好.像这样的东西使它变绿:

something.act = function() { require('my_module').someFunction(); };
Run Code Online (Sandbox Code Playgroud)

现在来看看这个:

describe("something.act()", function() {
  it("calls the 'root' function of my module", function() {
    var mod = require('my_module');
    spyOn(mod); // jasmine needs a property name
                // pointing to a function as param #2
                // therefore, this call is not correct.
    something.act();
    expect(mod).toHaveBeenCalled(); // mod should be a spy
  });
});
Run Code Online (Sandbox Code Playgroud)

这是我想用此规范测试的代码:

something.act = function() { require('my_module')(); };
Run Code Online (Sandbox Code Playgroud)

在过去的几个月里,这让我陷入困境几次.一个理论解决方案是替换require()并返回使用createSpy()创建的间谍.但是require()是一个不可阻挡的野兽:它是每个源文件/模块中函数的不同"副本".在规范中对它进行存根不会取代"testee"源文件中的真实require()函数.

另一种方法是在加载路径中添加一些假模块,但它看起来太复杂了.

任何的想法?

es1*_*128 6

重新布线非常棒

var rewire = require('rewire');

describe("something.act()", function() {
  it("calls the 'root' function of my module", function() {
    var mod = rewire('my_module');
    var mockRootFunction = jasmine.createSpy('mockRootFunction');
    var requireSpy = {
      mockRequire: function() {
        return mockRootFunction;
      }
    };
    spyOn(requireSpy, 'mockRequire').andCallThrough();

    origRequire = mod.__get__('require');
    mod.__set__('require', requireSpy.mockRequire);

    something.act();
    expect(requireSpy.mockRequire).toHaveBeenCalledWith('my_module');
    expect(mockRootFunction).toHaveBeenCalled();

    mod.__set__('require', origRequire);
  });
});
Run Code Online (Sandbox Code Playgroud)


jbp*_*ros 5

看起来我找到了一个可接受的解决方案.

规范助手:

var moduleSpies = {};
var originalJsLoader = require.extensions['.js'];

spyOnModule = function spyOnModule(module) {
  var path          = require.resolve(module);
  var spy           = createSpy("spy on module \"" + module + "\"");
  moduleSpies[path] = spy;
  delete require.cache[path];
  return spy;
};

require.extensions['.js'] = function (obj, path) {
  if (moduleSpies[path])
    obj.exports = moduleSpies[path];
  else
    return originalJsLoader(obj, path);
}

afterEach(function() {
  for (var path in moduleSpies) {
    delete moduleSpies[path];
  }
});
Run Code Online (Sandbox Code Playgroud)

规格:

describe("something.act()", function() {
  it("calls the 'root' function of my module", function() {
    var mod = spyOnModule('my_module');
    something.act();
    expect(mod).toHaveBeenCalled(); // mod is a spy
  });
});
Run Code Online (Sandbox Code Playgroud)

这并不完美,但工作做得很好.它甚至没有考虑到被测试者的源代码,这对我来说是一种标准.