[Rspec-devel] BDD with Rails, deciding on context boundaries
luke at agileevolved.com
Tue Aug 15 08:41:39 EDT 2006
I've been working with RSpec on my latest project and I'm currently
writing a 5 part article about testing Rails apps properly and I plan
on mentioning BDD and RSpec. First of all though, I want to clear up
some of my own confusion. I believe *may* have been focussing on
testing state in my specs rather than behaviour and I'd like advice
on how to improve what I'm doing if I am indeed going in the wrong
Let me focus on model tests for the moment. Lets take a User model. I
find that the first few contexts for an ActiveRecord model in Rails
tend to be the same, generally a "New foo" and a "Saved foo". I try
to avoid testing built-in AR features such as find/destroy/create as
this should be covered by AR's own tests. Here's some specs for a
User model on my current project:
Does this look like the right approach to take? If not how could
these specs be better - more BDD-like? Instead of this:
context "New user" do
specify "should not be valid" do
Should I be doing something more like this:
context "New user" do
specify "should return false when calling valid" do
I guess the main difference between the two is the first looks more
like a user story whereas the second looks more like an explicit test
of the code's behaviour (rather than the behaviour of the conceptual
model itself). Which is more correct?
The next thing I want to discuss is controller specs. First of all, I
think a Rails controller is far too wide in scope to focus on as a
whole - its always irked me that Rails' unit test stubs for
controllers (*ahem* sorry *functional* tests if we believe Rails'
terminology) generate a stub testcase for the entire controller. I
instead prefer to concentrate on the individual actions.
So lets take, for example, a UserController#create action. Now how
can we break this down into contexts? One way of looking at
controller actions is a conceptual "object" that you send "messages"
to, like normal objects, except in this case the "object" is really
just a controller method and the "messages" are HTTP Verbs and
parameters. So would we split the contexts up by verb? But what about
different results - a create could be successful or it could fail.
Where does this fit into things? One way of breaking it up would be
User creation via GET
- should never create a user
- should flash an "invalid request" message
- should redirect back to the previous page
Successful user creation via POST
- should create a new user
- should flash a success message
- should redirect to the user list page
Failed user creation via POST
- should not create a new user
- should flash an error message
- should redirect back to the new user page
Would this be how you would break it down? Also, do you believe these
specs are the place to specifiy the behaviour of the resulting view
as well as the action behaviour? Or should the view behaviours have
their own specs entirely? Or is test::unit and rspec the wrong place
to try and test your view output - would an acceptance testing tool
like selenium or watir be more suited to the task?
Finally, what are your thoughts on simulating the above situations
like successful/failed creations by stubbing out ActiveRecord methods
like save() (the Stubba library that comes with Mocha -
mocha.rubyforge.org - is particularly good at doing this)? I feel
that by simulating successful/failed requests by passing the
appropriate parameters, you are coupling your controller tests to
your model's implementation - i.e. if what makes a model valid
changes, you also need to change your controller tests, which seems
like a bad smell to me.
What would be really nice is to use mocks so that your model *and*
controller tests never touch the database. Believe it or not,
ActiveRecord doesn't make this easy - ActiveRecord barfs if you send
it a mock when it expects another ActiveRecord object - it explicitly
type checks. So much for duck typing!
Thanks for listening to my semi-coherent ramblings!
More information about the Rspec-devel