Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Mongoid::Observer with rspec

On a simple mongoid data model with a user that has many comments, I want to award the user with a specific badge when he writes at least 1 comment. So I set up an observer like this :

class CommentBadgeObserver < Mongoid::Observer
  observe :comment

  def after_create(comment)
    CommentBadge.check_conditions_for(comment.user)
  end
end

class CommentBadge < Badge
  def self.check_conditions_for(user)
    if user.comments.size > 1
      badge = CommentBadge.create(:title => "Comment badge")
      user.award(badge)
    end
  end
end

The user.award method :

def award(badge)
  self.badges << badge
  self.save
end

The following test fails (but I guess it is normal because observers are executed in background ?)

it 'should award the user with comment badge' do
    @comment = Factory(:comment, :user => @user)
    @user.badges.count.should == 1
    @user.badges[0].title.should == "Comment badge"
end

What could be the best way to validate this behavior ?

like image 492
obo Avatar asked Nov 22 '11 09:11

obo


1 Answers

I have got a working stand-alone adaptation of your code (see below). I had to make three small changes to get it working the way you were expecting.

To get the Observer working at all you have to instantiate it. In my example I needed to add the lines:

Mongoid.observers = CommentBadgeObserver
Mongoid.instantiate_observers

In Rails you can achieve the same thing adding this to config/application.rb (according to the docs):

config.mongoid.observers = :comment_badge_observer

I think there is also a small logic error in CommentBadge.check_conditions_for, the > 1 should be > 0.

Finally I changed the User#award method to save the badge rather than the user, because the 'foreign key' field that stores the relationship is on the badge side.

class Comment
  include Mongoid::Document
  field :name
  belongs_to :user
end

class CommentBadgeObserver < Mongoid::Observer
  observe :comment

  def after_create(comment)
    CommentBadge.check_conditions_for(comment.user)
  end
end

class Badge
  include Mongoid::Document
  field :title
  belongs_to :user
end

class CommentBadge < Badge
  def self.check_conditions_for(user)
    if user.comments.size > 0
      badge = CommentBadge.create!(:title => "Comment badge")
      user.award(badge)
    end
  end
end

class User 
  include Mongoid::Document
  field :first_name
  has_many :comments
  has_many :badges

  def award(badge)
    self.badges << badge
    badge.save!
  end
end

Factory.define(:user) do |u|
  u.first_name 'Bob'
end

Factory.define(:comment) do |c|
  c.name 'Some comment...'
end

# Observers need to be instantiated
Mongoid.observers = CommentBadgeObserver
Mongoid.instantiate_observers

describe CommentBadgeObserver do
  it 'should create badges' do
    @user = Factory.build(:user)
    @comment = Factory(:comment, :user => @user)
    @user.badges.count.should == 1
    @user.badges[0].title.should == "Comment badge"
  end
end
like image 55
Steve Avatar answered Nov 04 '22 01:11

Steve