использование sinon js для проверки функции, которая вызывает new для зависимости - PullRequest
0 голосов
/ 14 февраля 2020

Я ужасно смущен и хотел бы помочь.

Я не думаю, что этот код плохо спроектирован, но поправьте меня, если вы видите недостаток.

Кажется, я не могу понять, как протестировать функцию, которая вызывает new на зависимости. Вот мои укороченные классы .... с ненужными бизнес-логиками c раздетые.

MyWrapper. js

const SomeLib = require('SomeLib');

module.exports.MyWrapper = class MyWrapper {
    constructor(username, password) {
        this._someLib = new SomeLib(username, password);
    }

    async getFoo(id) {
        // other business logic omitted
        // which was tested in the MyWrapper.spec.js
        return this._someLib.findById(id)
    }

    async saveFoo(object) {
        // other business logic omitted
        // which was tested in the MyWrapper.spec.js
        return this._someLib.save(object)
    }
}

MyApp. js

const MyWrapper = require('MyWrapper');

const process = async (message) => {
    const wrapper = new MyWrapper(process.env.username, process.env.password)
    // some business logic around the message...omitted
    const data = wrapper.getFoo(message.id);
    if(data) {
       // do stuff with the data
       wrapper.saveFoo(data);
    } else {
       console.log('no data found for message', message);
    }
}
module.exports = { process }

MyApp.spe c. js

const sut = require('MyApp');

describe('MyApp', function(){
    describe('process', function(){
        it('should not call save when no data found', async function(){
             // how do I prevent 'new MyWrapper(process.env.username, process.env.password)' from being called???
             // how do I stub "wrapper.getFoo(message.id)" so I can return "undefined"
             const msg = {......};
             await sut.process(msg);
             // now assert wrapper.getFoo was called
             // now assert wrapper.saveFoo was *NOT* called
        });

        it('should call save when data is found', async function(){
             // how do I prevent 'new MyWrapper(process.env.username, process.env.password)' from being called???
             // how do I stub "wrapper.getFoo(message.id)" so I can return some JSON
             const msg = {......};
             await sut.process(msg);
             // now assert wrapper.getFoo was called
             // now assert wrapper.saveFoo was called
        });
    });
});

Ответы [ 2 ]

0 голосов
/ 18 февраля 2020

Нам, вероятно, нужно установить и включить proxyquire библиотеку в заглушку MyWrapper реализации.

MyApp.spec.js

const proxyquire = require("proxyquire"); // include the library here
const sinon = require("sinon");
const chai = require("chai");
const sinonChai = require("sinon-chai");
chai.should();
chai.use(sinonChai);

describe("MyApp", function() {
  describe("process", function() {
    beforeEach(() => {
      sinon.restore(); // best practice to always restore stub
    });

    it("should not call save when no data found", async function() {
      // stub getFoo to return undefined
      const getFooStub = sinon.stub().returns(undefined);
      const saveFooStub = sinon.stub();

      // create wrapper function to return stub
      function MyWrapperStub() {
        return {
          getFoo: getFooStub,
          saveFoo: saveFooStub
        };
      }

      // I don't know why your code use require('MyWrapper'), it should be prefixed with `./`
      // Here is basically we override `./MyWrapper` in our `MyApp.js` with the stub
      const sut = proxyquire("./MyApp", { "./MyWrapper": MyWrapperStub });

      const msg = { id: "123" };
      await sut.process(msg);

      getFooStub.should.have.been.calledWith("123");
      saveFooStub.should.not.have.been.called;
    });

    it("should call save when data is found", async function() {
      // stub getFoo to return json data
      const data = { name: "something" };
      const getFooStub = sinon.stub().returns(data);
      const saveFooStub = sinon.stub();

      function MyWrapperStub() {
        return {
          getFoo: getFooStub,
          saveFoo: saveFooStub
        };
      }
      const sut = proxyquire("./MyApp", { "./MyWrapper": MyWrapperStub });

      const msg = { id: "123" };
      await sut.process(msg);

      getFooStub.should.have.been.calledWith("123");
      saveFooStub.should.have.been.calledWith(data);
    });
  });
});

В MyApp.js я изменил реализацию require на:

const MyWrapper = require('./MyWrapper');

Если вы продолжаете настаивать на использовании require('MyWrapper'), тогда в тестовом файле прокси-сервер должен быть установлен на proxyquire("./MyApp", { "MyWrapper": MyWrapperStub });

Снимок экрана:

enter image description here

Надеюсь, это поможет

Ref:

0 голосов
/ 15 февраля 2020

глядя на ваш код в том виде, в котором он в настоящее время структурирован, вы не сможете не вызвать new MyWrapper, потому что этот вызов выполняется внутри функции, которую вы вызываете. Скорее всего, вы захотите использовать внедрение зависимостей для передачи экземпляра MyWrapper. Использование DI также позволит вам передавать макетированную версию вашего класса Wrapper для большего контроля над тестированием. Это может выглядеть примерно так:

// MyApp.js

const MyWrapper = require('MyWrapper');

const wrapper = new MyWrapper(process.env.username, process.env.password);

const process = async (message, wrapper) => {
    // some business logic around the message...omitted
    const data = wrapper.getFoo(message.id);
    if(data) {
       // do stuff with the data
       wrapper.saveFoo(data);
    } else {
       console.log('no data found for message', message);
    }
}
module.exports = { process }

Возможно, вы захотите создать экземпляр оболочки в другом месте, которое имеет смысл для вашего приложения, но на основе представленного вами кода это теперь позволит вам передать свой собственный тестируйте версию класса Wrapper при тестировании, например так:

// MyApp.spec.js

const sut = require('MyApp');

describe('MyApp', function(){
    describe('process', function(){
        it('should not call save when no data found', async function(){
             // how do I prevent 'new MyWrapper(process.env.username, process.env.password)' from being called???
             // how do I stub "wrapper.getFoo(message.id)" so I can return "undefined"
             const msg = {......};
             const wrapper = new MyWrapper();
             await sut.process(msg, wrapper);
             // now assert wrapper.getFoo was called
             // now assert wrapper.saveFoo was *NOT* called
        });

        ...
});

Вторая часть вашего вопроса касается создания заглушки класса с использованием sinon. Документация для sinon, найденная здесь здесь , вероятно, даст вам лучший пример того, как создать заглушку и вернуть ваше собственное значение из класса заглушки.

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...