TDD is A Good Thing

by Justin Blake on February 2, 2010

I always sorta kinda did TDD. But not really. It never really clicked for me until several months ago and before that I didn’t even realize that it hadn’t clicked yet. The other day I wrote about pairing with Corey Haines and something that he said that really hit home: When I write my tests, I’m writing the code I wish I had. This almost forces you to write clean, well-designed code.

For example, if I know I need active products, and I start with the implementation, of course I’m going to call Product.find and pass whatever parameters I need to make that happen. But if I start with the test, I really don’t want to set all those expectations. I’d much rather stub a tiny method and check that I get the expected results.

But now I have another problem. I stubbed the model method, implemented the code in my controller, and have a fully-passing test suite, but my controller is calling a method that doesn’t even exist yet! That’s where the final piece of the TDD puzzle fell in place for me:

Cucumber is a Very Good Thing

Cucumber lets you create high-level tests that hit your entire stack. This means I can write a test that will hit an action as if it was a normal web request, and it will go through all the steps it would go through if it was serving it up to a normal browser (and depending on the engine you use, it may be). No stubs. A real request.

So my nice clean controller action that passes beautifully in rspec? Fails miserably in cucumber. Good!

It seemed strange at first to have two separate testing frameworks in my workflow, but once it clicked, it felt just right. Cucumber is what drives my features. Rspec is what drives my code. In other words, rspec is testing that my code does what it’s supposed to do at a nice, low, isolated level. Cucumber tests that my application is doing what it’s supposed to do at a nice, high, functional level.

Bookmark and Share

Other Posts That Might Interest You

  1. Mocking is dead. Long live mocking!
  2. Factories for test objects. Use them.
  3. Subdomains, SubdomainFu, and Cucumber
  • bewhite
    Very short version of your article: Rspec defines HOW your application should work (geek point of view). Cucumber defines WHAT your application should do (simple user point of view).

    Cucumber is really much better covers user's interests but it is not good enough:
    1) No JavaScript. Can you call at least one popular and modern web application without JavaScript and AJAX features?
    2) One engine to rule them all. Cucumber uses just one website engine. Probably quite good engine but there are quite big number of users that can use other browsers and other engines. For these users we can't be sure if all cucumber features still works fine.
    3) Flash. I'm not sure here but can Cucumber test Flash parts of our websites?

    It seems like too big text for comment. I'll create a post to my blog.
  • The latest version of cucumber can use many different engines, including ones that run in a real browser, and support javascript.

    I'm not sure about flash, but I don't think cucumber is the best place for testing that anyway.
  • When I stub a method, I make a note that I need to spec-drive the special case of that method that results in the behavior I stubbed. For example, when I stub Product.active() to return [], I spec-drive the implementation of active() for the case where it returns []. This way, I rely less on end-to-end tests, and I find I can more safely change my code.

    If you find yourself writing an overly-specific or overly-complex Cucumber scenario to force you to implement a method, try the technique I've just described. It might help.

    Welcome to the world of doing TDD *really*. :)
  • I'm not familiar with the term spec-drive. But it sounds interesting.

    Since I typically start with Cucumber, then drop down to Rspec, THEN drop down to writing actual code, I never really feel like I'm writing overly-specific Cucumber scenarios to try and get a method covered. I'm only ever writing methods to make specs and scenarios pass.
  • Let me ask you, then, because I don't get to watch other RSpec users work much: after you write a scenario, and you implement the happy path of a method like #active, when and how do you decide to handle variations or error paths on #active? Do you write the corresponding scenario?
  • I would continue writing specs and not pop back up into Cucumber until I feel a new method is sufficiently tested. I stick to the happy path with Cucumber unless the logic is complex or atypical.
  • That sounds sensible. I know some people who end up trying to write exhaustive Cucumber tests. It rarely ends well.
blog comments powered by Disqus

Previous post:

Next post: