Forum: RSpec best practice for model callbacks?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Barun S. (Guest)
on 2009-04-16 21:31
(Received via mailing list)
In many of my models, I have callback methods like this:

class MyModel < ActiveRecord::Base

  before_save   :do_something

  def do_something
    some code here...
  end

end

The do_something method is a public method that is sometimes called on
its
own, and also called whenever the model is saved.  Let's say there are
six
specs that are required to fully test the do_something method.  What is
considered best practice for how the before_save filter should be
tested?
If I say that I'm only ever going to test the behavior, it means that I
need
to basically rewrite those six specs to make sure they hold whenever the
model is saved, as well as when the do_something method is called
explicitly.  I don't like having to repeat myself this way, particularly
considering that a model may have multiple callbacks like this so I end
up
having to repeat myself a lot.  The alternative is to just do something
like:

x = MyModel.new
x.should_receive(:do_something)
x.save

But of course this tests implementation rather than behavior which is
usually not desirable.  But in this situation I'm tending to prefer it
over
having to do a ton of rewriting of specs.  What are others' thoughts on
this?

Thanks..
Barun S. (Guest)
on 2009-04-16 21:49
(Received via mailing list)
Oh, and I suppose the third option here could be to so something like:

[:do_something, :save].each do |method_name|
it "should satisfy some spec when we call the #{method_name} method" do
  x = MyModel.new
  x.call(method_name)
  x.should ... (whatever the condition is here)
end
end

I use this sort of technique at time to keep my specs dry, simple and
behavior-driven at the same time, but I find it can make the spec files
a
bit hard to read through so I try not to use it unless it is really
needed...
Mike S. (Guest)
on 2009-04-16 21:54
(Received via mailing list)
In cases like this I write tests for the method, in your case
do_something,
and then spec the behavior that a filter exists which calls that method.
No
more, no less. You can safely assume that ActiveRecord itself has tests
to
ensure a properly declared before filter will be called when the model
is
saved.

HTH,
Mike

On Apr 16, 2009 1:34 PM, "Barun S." <removed_email_address@domain.invalid> 
wrote:

In many of my models, I have callback methods like this:

class MyModel < ActiveRecord::Base

  before_save   :do_something

  def do_something
    some code here...
  end

end

The do_something method is a public method that is sometimes called on
its
own, and also called whenever the model is saved.  Let's say there are
six
specs that are required to fully test the do_something method.  What is
considered best practice for how the before_save filter should be
tested?
If I say that I'm only ever going to test the behavior, it means that I
need
to basically rewrite those six specs to make sure they hold whenever the
model is saved, as well as when the do_something method is called
explicitly.  I don't like having to repeat myself this way, particularly
considering that a model may have multiple callbacks like this so I end
up
having to repeat myself a lot.  The alternative is to just do something
like:

x = MyModel.new
x.should_receive(:do_something)
x.save

But of course this tests implementation rather than behavior which is
usually not desirable.  But in this situation I'm tending to prefer it
over
having to do a ton of rewriting of specs.  What are others' thoughts on
this?

Thanks..
Pat M. (Guest)
on 2009-04-16 22:19
(Received via mailing list)
What is the thing that's being done in a callback and also sometimes
called by clients?  Usually the semantics are different and you don't
want to treat them exactly the same...

At any rate, you can be creative with shared example groups to get rid
of the duplication.  Something like

describe "do_something", :shared => true do
  it "should update the posts_count" do
    lambda { do_action }.
      should change(subject, :posts_count).by(1)
  end

  it "should send an email" do
    do_action
    #  however you get emails..I forget
  end
end

describe Foo, "when saved" do
  it_should_behave_like "do_something"

  subject { new_foo }

  def do_action
    subject.save!
  end
end

describe Foo, "when do_something is called" do
  it_should_behave_like "do_something"

  subject { new_foo }

  def do_action
    subject.do_something
  end
end

Does that help?

Pat
Barun S. (Guest)
on 2009-04-16 22:40
(Received via mailing list)
Yes that does help, thanks!  I didn't know about shared example groups.
Mike S. (Guest)
on 2009-04-17 15:03
(Received via mailing list)
Errr... before callback. I've been spending too much time with
controllers.
:-)

On Apr 16, 2009 1:34 PM, "Barun S." <removed_email_address@domain.invalid> 
wrote:

In many of my models, I have callback methods like this:

class MyModel < ActiveRecord::Base

  before_save   :do_something

  def do_something
    some code here...
  end

end

The do_something method is a public method that is sometimes called on
its
own, and also called whenever the model is saved.  Let's say there are
six
specs that are required to fully test the do_something method.  What is
considered best practice for how the before_save filter should be
tested?
If I say that I'm only ever going to test the behavior, it means that I
need
to basically rewrite those six specs to make sure they hold whenever the
model is saved, as well as when the do_something method is called
explicitly.  I don't like having to repeat myself this way, particularly
considering that a model may have multiple callbacks like this so I end
up
having to repeat myself a lot.  The alternative is to just do something
like:

x = MyModel.new
x.should_receive(:do_something)
x.save

But of course this tests implementation rather than behavior which is
usually not desirable.  But in this situation I'm tending to prefer it
over
having to do a ton of rewriting of specs.  What are others' thoughts on
this?

Thanks..
This topic is locked and can not be replied to.