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

Rick DeNatale rick.denatale at gmail.com
Thu Dec 13 08:38:31 EST 2007


On 12/13/07, David Chelimsky <dchelimsky at gmail.com> wrote:

> 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.

I like this but, I'm running into a snag or two.

When I tried this as-is I'm getting a no method exception in
EquivalentMessage#== because it's running into a case where other is
:no_args. I put in some tracing and determined that it was also
getting other with the right message.

So (biting my lip because I don't like class tests I changed this to:

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

Now it fails with:

Spec::Mocks::MockExpectationError in 'Message from anyone should be
sent on save'
Mock 'Class' expected :deliver_secret_santa with
(#<EquivalentMessage:0x3519bec @message=#<Message id: nil, subject:
"Subj", body: "hi", sender_id: 343839476, recipient_id: 21341157,
message_type: 3, created_at: nil, updated_at: nil>>) once, but
received it twice

Now I'm interpreting this to mean that

1) the deliver_secret_santa message actually got called at least once
with no arguments, hence the :no_args

and

2) It got called twice with matching args.

As to the second, there's only one call to that method, in the
after_create call-back, and it passes the message as an argument.

So I put some tracing into the callback to print out a back trace, and
it does seem to be called twice, BUT it also seems that the
Message.create call IN THE SPEC, is being called twice?!?

**** after_create #<Message:0x3385808>
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:311:in
`call'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:311:in
`callback'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:304:in
`each'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:304:in
`callback'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:227:in
`create_without_timestamps'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/timestamp.rb:29:in
`create'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:2165:in
`create_or_update_without_callbacks'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:213:in
`create_or_update'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:1899:in
`save_without_validation'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/validations.rb:901:in
`save_without_transactions'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:80:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:100:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:120:in
`rollback_active_record_state!'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:522:in
`create'
./spec/models/message_spec.rb:177
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:18:in
`instance_eval'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:18:in
`run_in'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/matchers.rb:143:in
`capture_generated_description'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:17:in
`run_in'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_methods.rb:14:in
`execute'
/opt/local/lib/ruby/1.8/timeout.rb:48:in `timeout'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_methods.rb:11:in
`execute'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:260:in
`execute_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:258:in
`each'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:258:in
`execute_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:115:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:22:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:21:in
`each'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:21:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/options.rb:86:in
`run_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/command_line.rb:19:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/bin/spec:3

**** after_create #<Message:0x3385808>
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:311:in
`call'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:311:in
`callback'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:304:in
`each'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:304:in
`callback'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:227:in
`create_without_timestamps'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/timestamp.rb:29:in
`create'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:2165:in
`create_or_update_without_callbacks'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/callbacks.rb:213:in
`create_or_update'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:1899:in
`save_without_validation'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/validations.rb:901:in
`save_without_transactions'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:80:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:100:in
`transaction'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:120:in
`rollback_active_record_state!'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/transactions.rb:108:in
`save'
/Users/rick/ssanta/vendor/rails/activerecord/lib/active_record/base.rb:522:in
`create'
./spec/models/message_spec.rb:177
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:18:in
`instance_eval'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:18:in
`run_in'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/matchers.rb:143:in
`capture_generated_description'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example.rb:17:in
`run_in'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_methods.rb:14:in
`execute'
/opt/local/lib/ruby/1.8/timeout.rb:48:in `timeout'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_methods.rb:11:in
`execute'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:260:in
`execute_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:258:in
`each'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:258:in
`execute_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/example/example_group_methods.rb:115:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:22:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:21:in
`each'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/example_group_runner.rb:21:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/options.rb:86:in
`run_examples'
/Users/rick/ssanta/vendor/plugins/rspec/lib/spec/runner/command_line.rb:19:in
`run'
/Users/rick/ssanta/vendor/plugins/rspec/bin/spec:3

Not sure why this is happening, but it seems to be the rspec machinery
that's doing it unless I'm missing something.

Here's the complete description from the spec:

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

   def ==(other)
     Message === 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


describe Message, "from anyone" do

  # t.string :subject
  # t.text :body
  # t.integer :from_id
  # t.integer :to_id
  # t.integer :type  # Normal, FromSecretSanta, ToSecretSanta

  fixtures :people

  before(:each) do
  end

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

end
-- 
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/


More information about the rspec-users mailing list