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/ BDD
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
  end
  def sort
    array.sort
  end
end

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

are you going to write
  @array.should_receive(:sort)

or
  @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 possible.

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.

Ashley


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

--
http://www.patchspace.co.uk/
http://aviewfromafar.net/



_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to