Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would you test observers with rSpec in a Ruby on Rails application?

Suppose you have an ActiveRecord::Observer in one of your Ruby on Rails applications - how do you test this observer with rSpec?

like image 885
Nicolai Reuschling Avatar asked Aug 28 '08 18:08

Nicolai Reuschling


People also ask

What is RSpec in Ruby on Rails?

RSpec is a framework that allows us to do that. The "R" stands for Ruby, and "Spec" is short for Specification. A specification is a detailed requirement that our code should meet. Or more formally, it's an executable example that tests whether a portion of code exhibits the expected behavior in a controlled context.

Is RSpec unit test?

RSpec is a unit test framework for the Ruby programming language. RSpec is different than traditional xUnit frameworks like JUnit because RSpec is a Behavior driven development tool. What this means is that, tests written in RSpec focus on the "behavior" of an application being tested.


2 Answers

You are on the right track, but I have run into a number of frustrating unexpected message errors when using rSpec, observers, and mock objects. When I am spec testing my model, I don't want to have to handle observer behavior in my message expectations.

In your example, there isn't a really good way to spec "set_status" on the model without knowledge of what the observer is going to do to it.

Therefore, I like to use the "No Peeping Toms" plugin. Given your code above and using the No Peeping Toms plugin, I would spec the model like this:

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 

You can spec your model code without having to worry that there is an observer out there that is going to come in and clobber your value. You'd spec that separately in the person_observer_spec like this:

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  

If you REALLY REALLY want to test the coupled Model and Observer class, you can do it like this:

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% of the time, I'd rather spec test with the observers turned off. It's just easier that way.

like image 193
Pete Avatar answered Sep 28 '22 00:09

Pete


Disclaimer: I've never actually done this on a production site, but it looks like a reasonable way would be to use mock objects, should_receive and friends, and invoke methods on the observer directly

Given the following model and observer:

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 

I would write a spec like this (I ran it, and it passes)

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 
like image 42
Orion Edwards Avatar answered Sep 27 '22 22:09

Orion Edwards