Sinon: withArgs некорректно работает с sinon.mock

Созданный на 30 авг. 2017  ·  3Комментарии  ·  Источник: sinonjs/sinon

  • Версия Sinon: 3.2.1
  • Среда: OSX, узел 7
  • Другие библиотеки, которые вы используете: Mocha, mjackson / expect.

Чего вы ожидали?
Когда я создаю макет класса и пытаюсь управлять поведением одной из его функций с помощью withArgs , выдается исключение. Происходит только при использовании нескольких вызовов withArgs .

Что на самом деле происходит

Как воспроизвести


Воспроизводимый пример

Предположим, я объявил класс C :

      class C {
        static foo(arg1, arg2) {return 'foo';}
      }

Я пытаюсь контролировать поведение foo используя имитацию C :

      it('test withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        CMock.restore();
      });

Что вызывает следующее исключение:

ExpectationError: foo получил неверные аргументы ["a", "b"], ожидалось ["c", "d"]
в Object.fail (node_modules / sinon / lib / sinon / mock-expectation.js: 281: 25)
в функции.(node_modules / sinon / lib / sinon / mock-expectation.js: 182: 33)
в Array.forEach (собственный)
в Function.verifyCallAllowed (node_modules / sinon / lib / sinon / mock-expectation.js: 175: 27)
в Function.invoke (node_modules / sinon / lib / sinon / mock-expectation.js: 78: 14)
на прокси (node_modules / sinon / lib / sinon / spy.js: 89: 22)

Пока работает этот код:

      it('test withArgs stub', () => {
        let fooStub = sinon.stub(C, 'foo');

        fooStub.withArgs('a', 'b').returns(1);
        fooStub.withArgs('c', 'd').returns(2);

        expect(fooStub('a', 'b')).toEqual(1);
        expect(fooStub('c', 'd')).toEqual(2);

        fooStub.restore();
      });

Стоит отметить, что следующие сценарии работают должным образом:

      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('a', 'b')).toEqual(1); // ok

        CMock.restore();
      });
      it('test one withArgs mock', () => {
        let CMock = sinon.mock(C);
        let fooStub = CMock.expects('foo');

        fooStub.withArgs('a', 'b').returns(1);

        expect(fooStub('c', 'd')).toEqual(1); // error: foo received wrong arguments ["c", "d"], expected ["a", "b"]

        CMock.restore();
      });

Это означает, что проблема возникает только при многократном использовании withArgs .
Это может быть связано с # 1381, но, как вы можете видеть в примере, предложенное там решение не работает.

Bug Medium Help wanted stale

Самый полезный комментарий

Я взглянул на код.
Прежде всего, этот тест работает:

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

Это работает по двум причинам, а тест в исходном выпуске - нет:

  • Создаю два expects .
  • Я вызываю статический метод вместо метода, возвращаемого expects .

Значит, это тоже не работает:

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

Почему это происходит?

Каждый раз, когда вызывается expects , происходит следующее:

  • Создается новое ожидание (заглушка, дополненная minCalls/maxCalls - переменные, которые изменяются, когда вы вызываете, например, once() ), только то, что это «заглушка с заданной областью», но не иметь связь с другими expects . Это объект, возвращенный из expects .
  • Это добавлено в список ожиданий.

Что приводит к разному поведению для этих сценариев:

  • Если вы вызовете метод, возвращаемый expects , он будет ограничен только этим expects .
  • Если вы вызываете метод напрямую, а не заглушку, он будет искать по всем expects и найдет подходящий. Если их несколько, он вызовет первый.

Это правильное поведение? Мне кажется более интуитивным, что как только кто-то вызывает expects для метода, он будет вести себя точно так же, как stub (с дополнительными minCalls/maxCalls ). Это приведет к:

  • Дополнительный код может быть разделен между stub и mock-expectation .
  • Поведение описанных выше сценариев будет таким же.

А что будет с minCalls/maxCalls ?

Поскольку сейчас есть только один expectation , я предлагаю три альтернативных способа проверки количества вызовов с помощью моков:

  • Разрешить объединение once() , twice() и т. Д.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Хотя, возможно, это немного сбивает с толку.

  • Не разрешать объединять once() , twice() и т. Д.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

Более ясно, но вы не можете заглушить foo более одного раза, поэтому нам понадобится способ обойти это. Также это все еще возможно:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Используйте Spy API, который запоминает количество звонков.

Все 3 Комментарий

Спасибо за примерный отчет о проблеме. Я не использую имитацию, потому что это причиняет боль моему мозгу, поэтому кому-то еще нужно будет взглянуть на это, но ваши примеры должны сделать это намного проще.

Не стесняйтесь взглянуть на код самостоятельно - часто это самый быстрый способ исправить это :-)

Я взглянул на код.
Прежде всего, этот тест работает:

class C {
  static foo(arg1, arg2) {return 'foo';}
}

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(C.foo('a', 'b')).toEqual(1);
  expect(C.foo('c', 'd')).toEqual(2);

  CMock.restore();
});

Это работает по двум причинам, а тест в исходном выпуске - нет:

  • Создаю два expects .
  • Я вызываю статический метод вместо метода, возвращаемого expects .

Значит, это тоже не работает:

it('test withArgs mock', () => {
  let CMock = sinon.mock(C);

  CMock.expects('foo').withArgs('a', 'b').returns(1);
  let fooStub = CMock.expects('foo').withArgs('c', 'd').returns(2);

  expect(fooStub('a', 'b')).toEqual(1); // Error: foo received wrong arguments ["a", "b"], expected ["c", "d"]
  expect(fooStub('c', 'd')).toEqual(2);

  CMock.restore();
});

Почему это происходит?

Каждый раз, когда вызывается expects , происходит следующее:

  • Создается новое ожидание (заглушка, дополненная minCalls/maxCalls - переменные, которые изменяются, когда вы вызываете, например, once() ), только то, что это «заглушка с заданной областью», но не иметь связь с другими expects . Это объект, возвращенный из expects .
  • Это добавлено в список ожиданий.

Что приводит к разному поведению для этих сценариев:

  • Если вы вызовете метод, возвращаемый expects , он будет ограничен только этим expects .
  • Если вы вызываете метод напрямую, а не заглушку, он будет искать по всем expects и найдет подходящий. Если их несколько, он вызовет первый.

Это правильное поведение? Мне кажется более интуитивным, что как только кто-то вызывает expects для метода, он будет вести себя точно так же, как stub (с дополнительными minCalls/maxCalls ). Это приведет к:

  • Дополнительный код может быть разделен между stub и mock-expectation .
  • Поведение описанных выше сценариев будет таким же.

А что будет с minCalls/maxCalls ?

Поскольку сейчас есть только один expectation , я предлагаю три альтернативных способа проверки количества вызовов с помощью моков:

  • Разрешить объединение once() , twice() и т. Д.
  let fooStub = CMock.expects('foo').withArgs('a', 'b').returns(1).twice();
  fooStub.withArgs('c', 'd').returns(2).once(); // Does not affect the previous expectation.

Хотя, возможно, это немного сбивает с толку.

  • Не разрешать объединять once() , twice() и т. Д.
  CMock.expects('foo').withArgs('a', 'b').returns(1).twice(); // `twice()` returns `null`, so that one has to call `expects()` again to set expectations for other arguments. 
  CMock.expects('foo').withArgs('c', 'd').returns(2).once();

Более ясно, но вы не можете заглушить foo более одного раза, поэтому нам понадобится способ обойти это. Также это все еще возможно:

  CMock.expects('foo').withArgs('a', 'b').returns(1).withArgs('c', 'd').returns(2).twice(); // I guess that this should only affect the last `withArgs`?
  • Используйте Spy API, который запоминает количество звонков.

Эта проблема была автоматически помечена как устаревшая, поскольку в последнее время не было активности. Он будет закрыт, если больше не будет активности. Спасибо за ваш вклад.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги