[rspec-users] "Tricks" for testing after_create callback???

David Chelimsky dchelimsky at gmail.com
Thu Dec 13 00:16:36 EST 2007


On Dec 12, 2007 9:48 PM, Kyle Hargraves <philodespotos at gmail.com> wrote:
>
> On Dec 12, 2007 9:09 PM, Rick DeNatale <rick.denatale at gmail.com> wrote:
> > I've got a model Message, which needs to send an email using action
> > mailer after it's first saved in the database.
> >
> > I want to pass the model to the mailer which then uses methods on the
> > message model to render the email.
> >
> > So the natural way to do this is in an after_create callback on the
> > Message model.
> >
> > But I can't see an easy way to test this. Here's my spec
> >
> > describe Message, "from anyone" do
> >
> >   it "should be sent on save" do
> >     msg_creation_parms = {
> >       :subject => "Subj",
> >       :body => "hi",
> >       :sender => people(:rick),
> >       :recipient => people(:john)
> >       }
> >     SantasMailbox.should_receive(:deliver_secret_santa).with(Message.new(msg_creation_parms))
> >     Message.create(msg_creation_parms)
> >   end
> >
> > end
> >
> > This fails, but only because the model object has an id and time
> > stamps assigned as it's saved.
> >
> > Spec::Mocks::MockExpectationError in 'Message from anyone should be
> > sent on save'
> > Mock 'Class' expected :deliver_secret_santa with (#<Message id: nil,
> > subject: "Subj", body: "hi", sender_id: 343839476, recipient_id:
> > 21341157, message_type: nil, created_at: nil, updated_at: nil>) but
> > received it with (#<Message id: 1, subject: "Subj", body: "hi",
> > sender_id: 343839476, recipient_id: 21341157, message_type: nil,
> > created_at: "2007-12-12 21:53:16", updated_at: "2007-12-12 21:53:16">)
> >
> > I figured I'd through this out to the list for ideas on how best to
> > approach this before I go to bed and sleep on it. <G>
>
> Someone else may have a more elegant approach, but the block syntax to
> should_receive() could allow something like:
>
> expected_message = Message.new(msg_creation_params)
> SantasMailbox.should_receive(:deliver_secret_santa) do |msg|
>   msg.body.should == expected_message.body
>   msg.subject.should == expected_message.subject
>   # etc. if it's necessary
> end
> Message.create(msg_creation_params)

Not only could it support this, it DOES.

That said, I'd go for a lesser known feature: custom mock argument
matchers. Something like this (completely off the top of my head and
not tested or guaranteed bug-free - but this will give you the idea):

class EquivalentMessage
  def initialize(message)
    @message = message
  end

  def ==(other)
    other.subject == @message.subject &&
    other.body == @message.body &&
    other.sender == @message.sender &&
    other.recipient == @message.recipient
  end
end

def message_equivalent_to(message)
  EquivalentMessage.new(message)
end

it "should be sent on save" do
  msg_creation_parms = {
    :subject => "Subj",
    :body => "hi",
    :sender => people(:rick),
    :recipient => people(:john)
    }
  SantasMailbox.should_receive(:deliver_secret_santa).
                with(message_equivalent_to(Message.new(msg_creation_parms)))
  Message.create(msg_creation_parms)
end

Try that out and see what you think.

Cheers,
David



>
> Feels clunky, though.
>
> Kyle
>
> _______________________________________________
> rspec-users mailing list
> rspec-users at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>


More information about the rspec-users mailing list