Как я могу заглушить конструктор в node.js, используя sinon в проекте Cloud Functions for Firebase?

Я использую GeoFire в проекте Cloud Functions for Firebase, который хочу протестировать.

В моем исходном коде GeoFire используется так:

GeoFire = request('geofire');

...

var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then( () => {
    console.log("Added store " + storeId + " to GeoFire" );
        return Promise.resolve();
    });

Мне нужно заглушить как вызов конструктора GeoFire, так и метод GeoFire().set().

Я старался:

const geofireStub = sinon.stub(GeoFire, 'set').resolves();

Но я получил ошибку:

TypeError: Cannot stub non-existent own property set

Я также пробовал:

const setStub = sinon.stub().resolves();
const geofireStub = sinon.stub(GeoFire).returns({set: setStub});

И я получаю сообщение об ошибке:

TypeError: Cannot stub non-existent own property set

Обе ошибки происходят на линии geofireStub.

Читая документацию sinon, я понял, что могу заглушить методы объекта. Однако в этом случае GeoFire не является объектом; это функция-конструктор. Поэтому я действительно не знаю, как я могу заглушить его, не имея связанного объекта.

Спасибо!


person Felipe Ferri    schedule 15.11.2017    source источник


Ответы (2)


Вам нужно что-то вроде этого, используя rewire:

// target.js
var GeoFire = require('geofire');

var catGeoFire = new GeoFire(catGeofireRef);
return catGeoFire.set(storeId, [lat, lon]).then(() => {
  console.log("Added store " + storeId + " to GeoFire" );
  return Promise.resolve();
});


// test.js
var GeoFire = require('geofire');
var rewire = require('rewire')
var target = rewire('./target')

describe('target', () => {
  it('test case', () => {
    // arrange

    // configure instance
    var geoFireStub = sinon.createStubInstance(GeoFire)
    geoFireStub.set.resolves()

    // configure constuctor
    var GeoFireMock = sinon.stub().returns(geoFireStub)

    // 'GeoFire' is a mocked variable here
    var revert = rewire('GeoFire', GeoFireMock)

    // act (call tested module)
    target()

    // assert (should is just for example)
    should(GeoFireMock).calledWithNew(/* params*/)
    should(geoFireStub.set).calledWith(/* params*/)

    //cleanup (rewire and stubs, prefer to use sandbox)
    revert();
    ...

  })
})

person Alexey Kucherenko    schedule 15.11.2017
comment
ооо хороший ответ, спасибо! Это работает! :-) Мне удалось решить проблему, создав дополнительный модуль, который требовал и экспортировал модуль GeoFire, поэтому я мог сделать что-то вроде: const geofireStub = sinon.stub(modules, 'GeoFire').returns({set: установить заглушку}). Это действительно сработало, но я думаю, что ваше решение лучше, а также, кажется, правильно интерпретирует, что такое заглушка и как ее создать. - person Felipe Ferri; 16.11.2017
comment
ой извините, я обнаружил, что я тестировал неправильно. Мой код работал, создавая новый модуль, который содержал модуль geofire, как я объяснил выше. Когда я изменил свой код, чтобы он был похож на ваш, я получил ошибку TypeError: Cannot read property 'resolves' of undefined из строки geoFireStub.set.resolves(). - person Felipe Ferri; 16.11.2017
comment
Вам нужно убедиться, что GeoFire является конструктором, а geoFireStub содержит все методы-прототипы (через заглушки). Просто используйте console.log, чтобы увидеть, что geoFireStub - person Alexey Kucherenko; 17.11.2017
comment
Я зарегистрировался в GeoFire и получил следующее описание: {[Функция: GeoFire] расстояние: [Функция]} - person Felipe Ferri; 21.11.2017
comment
не могли бы вы поделиться целевыми и тестовыми файлами через github gists? - person Alexey Kucherenko; 23.11.2017
comment
предпочитаю использовать песочницу — пример того, как многие, я уверен, оценят - person Lee Goddard; 05.05.2021

GeoFire — конструктор, а set — метод экземпляра. Вы должны заглушить GeoFire.prototype я считаю.

sinon.stub(GeoFire.prototype, 'set').resolves();
person Hitmands    schedule 15.11.2017
comment
Благодарю вас! Я пробовал это и все еще получаю ту же ошибку Cannot stub non-existing own property set. Кроме того, мне нужно будет заглушить сам вызов конструктора... - person Felipe Ferri; 15.11.2017