Pergunta

Suponha que você tenha um ActiveRecord::Observer em uma de suas aplicações Ruby on Rails - como testar esse observador com rSpec?

Foi útil?

Solução

Você está no caminho certo, mas encontrei vários erros frustrantes de mensagens inesperadas ao usar rSpec, observadores e objetos simulados.Quando estou testando meu modelo, não quero ter que lidar com o comportamento do observador em minhas expectativas de mensagem.

No seu exemplo, não há uma maneira realmente boa de especificar "set_status" no modelo sem saber o que o observador fará com ele.

Por isso, gosto de usar o Plug-in "Sem Peeping Toms". Dado o seu código acima e usando o plugin No Peeping Toms, eu especificaria o modelo assim:

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

Você pode especificar o código do seu modelo sem se preocupar com a possibilidade de haver um observador por aí que entrará e destruirá seu valor.Você especificaria isso separadamente em person_observer_spec assim:

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 

Se você REALMENTE deseja testar a classe acoplada Model e Observer, você pode fazer assim:

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% das vezes, prefiro testar as especificações com os observadores desligados.É mais fácil assim.

Outras dicas

Isenção de responsabilidade:Na verdade, nunca fiz isso em um site de produção, mas parece que uma maneira razoável seria usar objetos simulados, should_receive e amigos, e invocar métodos diretamente no observador

Dado o seguinte modelo e observador:

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

Eu escreveria uma especificação como esta (eu executei e ela passou)

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

no_peeping_toms agora é uma joia e pode ser encontrado aqui: https://github.com/patmaddox/no-peeping-toms

Se você quiser testar se o observador observa o modelo correto e recebe a notificação conforme o esperado, aqui está um exemplo usando RR.

seu_modelo.rb:

class YourModel < ActiveRecord::Base
    ...
end

seu_modelo_observador.rb:

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

    def custom_notification
        ...
    end
end

seu_modelo_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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top