Rails and TDD/BDD
I’m going to limit this post to my notes on the excellent learnings from the day of many testing talks at Rails Edge Chicago and follow up with another post for my addle-pated musings thereupon.
First I’d like to observe that Rails benefits heavily by the fact that Railers care passionately about BDD , coverage testing, seeking complete coverage, and beautiful, elegant code. In the past month at various Rails and non-Rails conferences, I’ve heard it hammered over and over again in many ways:
- You’ve got to have 100% coverage; anything less is uncertainty.
- It’s better to submit a test with no patch than a patch with no test.
- I pretty much don’t accept patches without a test.
…and many more. This shared mindset results in a community of developers who not only bring an ethic of constant improvement but also who seek to make code ownership irrelevant. More on that in the Conclusions.
Watching Jim Weirich do BDD was a religious experience. His talk was titled, “Red, Green, Refactor” which is the concise expression of his process: make a failing test, make the test pass, then do it again. He just sat down and started making tests, then writing just enough code to satisfy the test. It was design on the fly, iteratively building up the model in small increments, testing, and again; part of my joy from this was to watch as an excellent programmer exposed his process, even to the point of incorporating feedback from the room. His rake task for testing tracks the number of times it’s been called. If his talk is to be believed, he does it like this every day. And I believe.
Incidentally, Jim writes monster-long test names like “test_portfolio_with_one_stock_has_value_same_as_stock_value” to make his intent instantly readable. His point is that you’re only going to type this name once, so don’t let a desire for “manageable” method names get in the way of expressive, semantic naming.
And he made me realize how cool flexmock is: flex_mock_obj.should_receive(:quote).times(3).and_return(4,30,100)
David Chelimsky is the lead developer of RSpec, a very thoughtful person (as in “he thinks often and well”, though I’m sure he’s also kind =), and a vicious Werewolf waiting to happen. He did a half-hour speed talk about RSpec, which I’d only just heard about three weeks ago when talking to Matt Pelletier and again a couple of weeks ago at Ruby Hoedown. The general idea is you define your spec in a domain-specific language (DSL) which can then be executed to demonstrate that you meet spec. This was already on my list, but David’s moved it up quite a lot. He left me with some research:
- RBehave: story level behavior testing
- assert_select: find CSS items in page and assert about them.
- ZenTest/AutoTest : test like mad
- Test Notifications via Growl : combined with AutoTest, you can just watch things start working as you fix tests. Cool!
He also gave me reading: Domain Driven Design , by Eric Evans.
Mike Mangino’s talk, “Testing for the Real World”, made some excellent points:
- Fast tests get run.
- Slow tests don’t.
- The only tests that help are the ones that get run.
Actually, his talk seemed like a barrage of excellent points in retrospect; I ended up with a bunch of concise snippets in my notes which I’ll try to reconstitute into full explanations here. Apologies to Mike if I screw it up and libel his abilities; you should assume any errors are in my understanding or ability to explain the concept.
Fixtures don’t scale well for large quantities of data, so if you’re testing against a lot of data you’re best not using them. However, they’re alway useful as templates for test objects; for example, if you’re going to do a bunch of tests which all use the same data with only slight variations (testing each of many validators, &c) it can be handy to get the original object from a fixture and tweak it to suit your test.
Mocking can reveal the ugly spots when testing a controller. For example, if you find you need to mock a phonebook object to return an entry, an entry object to return a name, and a name object to return a last name … you’ve got issues in the code. In fact, it’s a good generalization that hard-to-test code very likely needs refactoring.
Meanwhile, it’s much easier to generate tests for views and controllers if you do them separately. Particularly, Mike recommends mocking helpers in view tests and testing the helpers separately.
Mike finally said the right words to make the difference between stubbing and mocking stick for me (yeah, I may be dumb but I’m slow). If the return value is important to the bit you’re testing, mock it; otherwise, stub it. He also mentioned that filters are hard to stub, but I didn’t catch an explanation so I’ll have to learn why on my own.
One of the best snippets: Tests should be informative first, DRY second. You want people who read your tests to instantly get it, not have to slog through various helper functions to figure out what’s going on.
Oh, and more research: CruiseControl, a continuous build/integration tool based on the work of Martin Fowler and Matthew Foemmel. When I tried to have a look at the Ruby/Rails version is seemed the server was not responding.
blog comments powered by Disqus