Почему этот синон-шпион не вызывается, когда я запускаю этот тест?

У меня есть модель магистрали:

class DateTimeSelector extends Backbone.Model

  initialize: ->
    @bind 'change:date', @updateDatetime
    @bind 'change:time', @updateDatetime

  updateDatetime: =>
    # do some stuff with the sate and time

И у меня есть несколько тестов для этого кода с использованием jasmin и sinon.js

describe "DateTimeSelector", ->
  beforeEach ->
    @datetime = new DateTimeSelector()

    describe "updateDatetime", ->
      beforeEach ->
        @updateSpy = sinon.spy(@datetime, 'updateDatetime')

      afterEach ->
        @datetime.updateDatetime.restore()

      # passes
      it "should be called when we call it", ->
        @datetime.updateDatetime()
        expect(@updateSpy).toHaveBeenCalledOnce()

      # fails
      it "should be called when we trigger it", ->
        @datetime.trigger 'change:date'
        expect(@updateSpy).toHaveBeenCalled()

      # fails
      it "should be called when we set the date", ->
        @datetime.set { date: new Date() }
        expect(@updateSpy).toHaveBeenCalled()

Кажется, это работает, когда я использую его в браузере, но я не могу пройти тесты. Кто-нибудь может просветить меня?


person David Tuite    schedule 09.12.2011    source источник
comment
Вы можете изменить тег, чтобы включить coffeescript. Я бы добавил его для вас, но у вас максимум 5, и я не хотел решать, какой из них заменить для вас.   -  person Kai    schedule 09.12.2011
comment
Да я никогда не знаю, что делать в этой ситуации. Очевидно, что вопрос написан в Coffee, но проблема и решение (скорее всего) не связаны с coffeescript. Поэтому я не знаю, правильно ли помечать как coffeescript.   -  person David Tuite    schedule 09.12.2011
comment
Ну, я посмотрел на этот вопрос, потому что он был помечен как JS; но я не могу помочь, потому что пример - coffeescript, который я сам не использую. Поэтому я подумал, что тег coffeescript может привлечь других пользователей coffeescript, которым будет легче читать и понимать ваш пример. :)   -  person Kai    schedule 09.12.2011
comment
Кажется, вы тестируете не ту вещь здесь. В большинстве случаев не стоит шпионить за классом, который вы хотите протестировать. В вашем случае вы должны проверить, является ли результат @updateDatetime тем, который вы ожидали, а не вызван ли он, потому что это функциональность, которую вы получаете от магистрали, и вы должны доверять им, что они проверили свои вещи.   -  person Andreas Köberle    schedule 10.12.2011


Ответы (2)


duckyfuzz, вы столкнулись с этой проблемой, потому что при создании шпиона (который фактически обертывает исходную функцию и создает уровень косвенности для вставки своих служб вызова метода отслеживания) привязка событий уже произошла. Это означает, что даже если шпион обернул исходную функцию, привязка события ссылается на исходную функцию, а не на обернутого шпиона. Следовательно, при тестировании исходная функция выполняется по триггеру события, но шпионское отслеживание находится на один уровень выше и не выполняется.

Чтобы убедиться, что привязка события на самом деле указывает на обернутую шпионскую функцию, вы должны создать шпиона перед созданием объекта модели (то же самое происходит, если вы тестируете представления). Для этого создайте шпион на прототипе "метода" класса:

в разделе beforeEach -> перед @datetime = new DateTimeSelector() создайте шпион: @updateSpy = sinon.spy(DateTimeSelector.prototype em>, 'updateDatetime')

обязательно измените раздел afterEach ->, в котором вы возвращаете прототип обратно в нормальное состояние, например: @updateSpy.restore()

это должен быть ваш код:

describe "DateTimeSelector", ->
  beforeEach ->
    @updateSpy = sinon.spy(DateTimeSelector.prototype, 'updateDatetime')
    @datetime = new DateTimeSelector()

  afterEach ->
    @updateSpy.restore()

  # passes
  it "should be called when we call it", ->
    @datetime.updateDatetime()
    expect(@updateSpy).toHaveBeenCalledOnce()

  # should pass now
  it "should be called when we trigger it", ->
    @datetime.trigger 'change:date'
    expect(@updateSpy).toHaveBeenCalled()

  # should pass now
  it "should be called when we set the date", ->
    @datetime.set { date: new Date() }
    expect(@updateSpy).toHaveBeenCalled() 

Кстати, если вы используете плагин jasmin-sinon.js, ваш синтаксис в порядке.

person vadimich    schedule 26.01.2012
comment
Это была именно моя проблема. Спасибо. - person EndangeredMassa; 13.04.2012
comment
Столкнулся с точно такой же проблемой, спасибо за правильный пример кода! - person berg; 17.07.2012

Вы смешиваете насмешливый синтаксис жасмина и синона.

В вашем проходном тесте ваш синон-шпион раскрывает свойство calledOnce, но вы используете функцию toHaveBeenCalledOnce() в стиле жасмина. Эта функция не существует в sinon spy, поэтому, по сути, не происходит никакого утверждения.

В своих неудачных тестах вы вызываете шпионскую функцию jasmine toHaveBeenCalled() на вашем шпионе sinon. У Жасмин есть свой синтаксис для создания шпиона: spyOn(obj, 'method');

person Eric Bock    schedule 15.12.2011