On Thu, Jan 7, 2010 at 5:09 AM, Matt W. [email protected] wrote:
corresponds to the controller where the behavior is implemented
pattern.
Wincent
directly itself because it has no actions.
OK I’m going to bite on this one.
I think that method probably works great for small models, but I have found
that over time on the Songkick codebase, I’ve come to rather dislike the
shared behaviours. This is mainly, I think, because when I get a failure,
the error message doesn’t point me very clearly at which class was actually
being tested when the failure occurred.
I don’t really see a correlation between size and usefulness of shared
behaviours either through rspec’s it_should_behave_like or a custom
macro. When you describe a class in a spec that class is prepended to
all of the example descriptions. For example:
describe “a bar”, :shared => true
it “should serve up tequila” { … }
end
describe Foo do
it_should_behave_like “a bar”
end
will produce:
Foo should serve up tequila
If the top-level describe for an object isn’t descriptive enough to
communicate what class/module/etc is being spec’ed then I see what
you’re saying as a problem because the backtrace of a failed shared
example won’t help either, since the backtrace stems from the file
that the shared example is stored in rather than the actual spec using
it (I wonder if there is a way around this).
I also think I resent them because I
know that for a popular module, I might be running the exact same specs over
the exact same code several times, for no purpose.
This doesn’t bother me a whole lot. While I don’t want to needlessly
run examples multiple times to ensure something is working, I like err
on the side of my objects are behaving correctly rather than an object
includes a module. One of my reasons for this is because in the past
I’ve unfortunately had to find the hard way classes that were buggy
because of module inclusion order and one module overwrote another
module’s method, yada yada yada. I have personally also gotten into a
groove for writing my own little shared macros and it makes it super
simple to build new classes which re-use that functionality and know
it actually works.
What I now prefer to do is keep the interface between a module (or even an
abstract class) and the class it’s mixed into quite clean, and specify that
re-usable unit in isolation, with an example using a temporary class created
in the test. I might have one or two specs in the class which mixes in the
module to prove that the module is mixed in and working, but most of the
specs for the behaviour of that module itself will live alongside the module
and run only once.
This is excellent advice regardless of using shared examples, although
it isn’t always practical (clean interfaces are always practical, what
isn’t is creating new specs with test classes). Some modules are
simply a grouping of certain behaviour that relies on other behaviour
existing on an object. Creating a new test class to put in a spec can
require a good amount of time and thinking based on the functionality
and where what you’re module’s functionality falls into the overall
chain of dependencies.
There are times to be disciplined and put in that time and thinking.
At other times, it’s important to not misplace a bunch of time and
energy if what you’re working on doesn’t warrant it. Unfortunately,
making knowing when to make good decisions often comes from making bad
ones. So as Corey H. would suggest, practice practice practice!
As David said earlier there are strengths and weaknesses to both approaches
so you have to find your own path here. I just wanted to share my
experience.
Ditto.
–
Zach D.
http://www.continuousthinking.com (personal)
http://www.mutuallyhuman.com (hire me)
http://ideafoundry.info/behavior-driven-development (first rate BDD
training)
@zachdennis (twitter)