John Feminella
2011-Jun-07 15:31 UTC
[rspec-users] Is "it_should_behave_like" a code smell?
Consider the following simple Rails app:
==== begin snippet ===# lib/herpable.rb
module Herpable; ...; end
# app/models/...
class ClassOne; include Herpable; end
class ClassTwo; include Herpable; end
# ...
==== end snippet ===
What''s the better way to write specs for these? Would you put the
module into its own shared_example?
==== begin snippet ===# spec/models/class_one_spec.rb
describe ClassOne do
it_should_behave_like "Herpable"
# ...
end
==== end snippet ===
Or would you just test the module directly?
==== begin snippet ===# spec/lib/herpable_spec.rb
describe Herpable do
let(:herped) { Class.new { include Herpable } }
it "should be derp" do
herped.should_be derp
end
==== end snippet ===
I started thinking about this because I noticed there seemed to be a
lot of specs running in our shared examples. That gave rise to a
couple of internal questions:
1.) If you have a bunch of closely related code that always gets
tested together, why isn''t it already a class or module?
2.) If it is, then why don''t you just spec that instead?
3.) If you do, then what''s the best way to use shared_examples_for /
it_should_behave_like?
~ jf
--
John Feminella
Principal Consultant, BitsBuilder
LI: http://www.linkedin.com/in/johnxf
SO: http://stackoverflow.com/users/75170/
David Chelimsky
2011-Jun-07 16:04 UTC
[rspec-users] Is "it_should_behave_like" a code smell?
On Jun 7, 2011, at 10:31 AM, John Feminella wrote:> Consider the following simple Rails app: > > ==== begin snippet ===> # lib/herpable.rb > module Herpable; ...; end > > # app/models/... > class ClassOne; include Herpable; end > class ClassTwo; include Herpable; end > # ... > ==== end snippet ===> > What''s the better way to write specs for these? Would you put the > module into its own shared_example? > > ==== begin snippet ===> # spec/models/class_one_spec.rb > describe ClassOne do > it_should_behave_like "Herpable" > # ... > end > ==== end snippet ===> > Or would you just test the module directly? > > ==== begin snippet ===> # spec/lib/herpable_spec.rb > describe Herpable do > let(:herped) { Class.new { include Herpable } } > > it "should be derp" do > herped.should_be derp > end > ==== end snippet ===> > I started thinking about this because I noticed there seemed to be a > lot of specs running in our shared examples. That gave rise to a > couple of internal questions: > > 1.) If you have a bunch of closely related code that always gets > tested together, why isn''t it already a class or module? > 2.) If it is, then why don''t you just spec that instead? > 3.) If you do, then what''s the best way to use shared_examples_for / > it_should_behave_like?There''s a section on this in The RSpec Book. Briefly: 1. Host objects can override behavior (intentionally or otherwise), so spec''ing the module outside a host is insufficient. For these cases, I recommend using shared examples. 2. Some modules do stuff (like validate stuff in the host) when they are included. For these cases I recommend spec''ing the module directly. See http://relishapp.com/rspec/rspec-core/dir/example-groups/shared-examples for different approaches. I like "it_behaves_like", or context-specific aliases. HTH, David