Как бы вы тестировали наблюдателей с помощью rSpec в приложении Ruby on Rails? - PullRequest
37 голосов
/ 28 августа 2008

Предположим, у вас есть ActiveRecord :: Observer в одном из ваших приложений на Ruby on Rails - как вы тестируете этого наблюдателя с помощью rSpec?

Ответы [ 4 ]

35 голосов
/ 25 сентября 2008

Вы на правильном пути, но я столкнулся с рядом неприятных неожиданных сообщений об ошибках при использовании rSpec, наблюдателей и фиктивных объектов. Когда я тестирую свою модель на спецификациях, я не хочу обрабатывать поведение наблюдателя в моих ожиданиях.

В вашем примере нет действительно хорошего способа спецификации "set_status" для модели без знания того, что с ней собирается делать наблюдатель.

Поэтому я хотел бы использовать плагин "No Peeping Toms". Учитывая приведенный выше код и плагин No Peeping Toms, я бы определил модель следующим образом:

describe Person do 
  it "should set status correctly" do 
    @p = Person.new(:status => "foo")
    @p.set_status("bar")
    @p.save
    @p.status.should eql("bar")
  end
end

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

describe PersonObserver do
  it "should clobber the status field" do 
    @p = mock_model(Person, :status => "foo")
    @obs = PersonObserver.instance
    @p.should_receive(:set_status).with("aha!")
    @obs.after_save
  end
end 

Если вы ДЕЙСТВИТЕЛЬНО ДЕЙСТВИТЕЛЬНО хотите проверить связанный класс Model и Observer, вы можете сделать это следующим образом:

describe Person do 
  it "should register a status change with the person observer turned on" do
    Person.with_observers(:person_observer) do
      lambda { @p = Person.new; @p.save }.should change(@p, :status).to("aha!)
    end
  end
end

99% времени, я бы предпочел спек-тест с отключенными наблюдателями. Так проще.

15 голосов
/ 29 августа 2008

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

Учитывая следующую модель и наблюдателя:

class Person < ActiveRecord::Base
  def set_status( new_status )
    # do whatever
  end
end

class PersonObserver < ActiveRecord::Observer
  def after_save(person)
    person.set_status("aha!")
  end
end

Я бы написал такую ​​спецификацию (я запустил ее, и она прошла)

describe PersonObserver do
  before :each do
    @person = stub_model(Person)
    @observer = PersonObserver.instance
  end

  it "should invoke after_save on the observed object" do
    @person.should_receive(:set_status).with("aha!")
    @observer.after_save(@person)
  end
end
4 голосов
/ 05 апреля 2011

no_peeping_toms теперь является драгоценным камнем, и его можно найти здесь: https://github.com/patmaddox/no-peeping-toms

2 голосов
/ 09 мая 2011

Если вы хотите проверить, что наблюдатель наблюдает правильную модель и получает уведомление, как и ожидалось, вот пример с использованием RR.

your_model.rb:

class YourModel < ActiveRecord::Base
    ...
end

your_model_observer.rb:

class YourModelObserver < ActiveRecord::Observer
    def after_create
        ...
    end

    def custom_notification
        ...
    end
end

your_model_observer_spec.rb:

before do
    @observer = YourModelObserver.instance
    @model = YourModel.new
end

it "acts on the after_create notification"
    mock(@observer).after_create(@model)
    @model.save!
end

it "acts on the custom notification"
    mock(@observer).custom_notification(@model)
    @model.send(:notify, :custom_notification)
end
...