Ваша проблема на самом деле не в sinon.spy
API, а в том, как функции импорта модулей Node. При вызове sinon.spy
, если мы не тестируем функцию обратного вызова, нам обычно требуется, чтобы объект был контекстом, в котором мы хотим шпионить за конкретным методом. Вот почему ваш пример пытается получить доступ к объекту exports
модуля foo.js
. Я не знаю, что Node предоставляет нам доступ к такому объекту.
Однако, чтобы ваш пример работал, нам не нужен доступ к exports
модуля Foo, мы могли бы просто создать контекст нашего своя. Например:
const chai = require("chai");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("foo", function () {
it('constructor was called.', function () {
const context = {
Foo: require("../app/foo"),
};
const fooSpy = sinon.spy(context, "Foo");
new context.Foo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});
Конечно, приведенный выше тест является рабочим решением проблемы, представленной в вашем примере, но в качестве теста он не очень полезен, потому что утверждение просто проверяет строку над ним. .
Шпионы более полезны, когда они являются зависимостями тестируемой системы (SUT). Другими словами, если у нас есть какой-то модуль, который должен создать Foo
, мы хотим сделать конструктор Foo
шпионом, чтобы он мог сообщить нашему тесту, что модуль действительно вызвал его.
Например, предположим, что у нас есть модуль fooFactory.js
:
const Foo = require("./foo");
module.exports = {
createFoo(num) {
return new Foo(num);
},
};
Теперь мы хотели бы создать модульный тест, который подтверждает, что вызов функции createFoo
модуля fooFactory.js
вызывает Foo
конструктор с указанным аргументом. Нам нужно переопределить зависимость fooFactory.js
Foo
с помощью шпиона.
Это возвращает нас к нашей исходной проблеме: как мы можем превратить импортированную функцию (конструктор) в шпион, если это не метод в объекте контекста, и поэтому мы не можем перезаписать его с помощью sinon.spy(context, 'method')
.
К счастью, мы не первые, кто столкнулся с этой проблемой. Существуют NPM модулей, которые позволяют переопределить зависимости в требуемых модулях. Sinon. js предоставляет How-To для выполнения таких действий, и они используют модуль под названием proxyquire .
proxyquire позволит нам импортировать fooFactory.js
в наш модульный тест, но также (и что более важно) переопределить Foo
, от которого он зависит . Это позволит нашему модульному тесту заставить fooFactory.js
использовать sinon.spy
вместо конструктора Foo
.
Тестовый файл станет:
const chai = require("chai");
const proxyquire = require("proxyquire");
const sinon = require("sinon");
const sinonChai = require("sinon-chai");
const expect = chai.expect;
chai.use(sinonChai);
describe("fooFactory", function () {
it("calls Foo constructor", function () {
const fooSpy = sinon.spy();
const { createFoo } = proxyquire("../app/fooFactory", {
"./foo": fooSpy,
});
createFoo(5);
expect(fooSpy).to.be.calledOnceWith(5);
});
});