[rspec-users] Test for a gem/plugin?

Ashley Moran ashley.moran at patchspace.co.uk
Wed Dec 10 12:13:59 EST 2008

On 10 Dec 2008, at 16:04, James Byrne wrote:

> Nonetheless, in the step definitions I must test the behaviour in a
> fashion which first drives and then confirms the implementation  
> details.

This is not the path to BDD, it's the path to state-based testing.   
Matt has already explained this well:

"How you chose to satisfy the user's need for that behaviour is an  
entirely separate matter.  A large benefit of having Acceptance Tests,  
IMO, is that you could radically change your implementation under the  
hood, such as switching to a different authentication mechanism, and  
the features would still be valid."

I'm guessing the customer for this project didn't ask for AuthLogic,  
they asked for part of the app to be protected.  You should only write  
acceptance tests for things these users care about.

> If the chosen implementation depends upon a third party plugin or gem
> then surely one should test for its provision?  What is the  
> alternative?
> Write tests to test that gems behaviour?  Does that not violate TDD/ 
> standard of only testing your own code?

Don't confuse library code with code you write to use this library.   
The AuthLogic config in your app is code too, and it adds behaviours  
to your app.  The fact there are 10, 100 or 10,000 lines of code  
behind it is irrelevant.

There's two attitudes you can take in spec files: write specs that  
your code calls some library, or write specs that describe the  
behaviour that library provides.

For example (the following is rushed, contrived code but illustrates  
the idea):

class Sorter
   def initialize(array)
     @array = array
   def sort

now when you spec
   @array = [5,9,6,1,4]
   @sorter = Sorter.new(@array)

are you going to write

   @sorter.sort.should == [1,4,5,6,9]

I believe that in this case, the second option is more valuble: you  
can change the implementation to sort other collections.  In the first  
case, you could refactor the code without breaking the existing spec.

This is important: *The ability to refactor code without breaking the  
specs is one of the major sources of value in the specs*.  This lets  
you reduce technical debt with no risk to your app.  If you rely on  
the implementation in your spec, you have to change the spec, and  
therefore you have no guarantee that the behaviour your users *asked  
for* still works.

The definition of refactoring is changing the implementation of code  
without changing its behaviour (and by extension its specs).  Without  
this constant your code will be fragile.

As a extreme, albeit small, example- a while back I took the code for  
a presentation I gave[1] and changed the app framework from Ramaze to  
Merb (it only took about 10 mins).  Because the specs all ran across  
the public HTML interface, there was no visible difference.  If the  
feature steps relied on the implementation this wouldn't have been  

I use Celerity[2] instead of Webrat and write all steps across an  
app's public interface for this reason.

> The imaginary end user of any non-trivial application is actually an
> assemblage of disparate roles, many of which consciously do not  
> overlap.
> Some of those users are the system administrators which tend to the  
> day
> to day background maintenance tasks, some of which are automated and
> some of which are not.  Some of the application users are, in truth,  
> the
> implementors themselves.

This is fine, but you should still write acceptance tests from the  
perspective of the user.  If the user is a shell script, drive your  
app on the command line.  If the user is a human, drive it over the  
visible interface.  But do you really have any users that access your  
app code directly?

Hope this gives another perspective on the subject.


[1] http://aviewfromafar.net/2008/10/2/geekup-sheffield-vi-from-specification-to-success
[2] http://celerity.rubyforge.org/


More information about the rspec-users mailing list