on test first
There are two things about “test first” I wanted to share.
1) I’ve been doing test first development for several years (as much as possible at least) and because of that I thought I was too good for “behavior driven development”.
Boy was I wrong. As it turns out, BDD is the bomb!
In my past life, while doing “test first” if I needed to implement an import feature, I would have tests like:
test_import_file
or something like that.
Well, that’s nice I guess, but what does that mean? Does the customer have any clue if I’m working on something he wants? Sure he knows that I’m working on something that imports files, but what is it? Why does he care? Don’t I need to write specs and documentation and vision documents and whatnot? (if you’re not sure, the answer is ‘hell no’)
When the customer expressed his requirements, he might have said something like:
The user should be able to trigger the import dialog from the main page. Then the user should then be able to select a local file and import it. Finally the user should see the contents of his file applied on the screen.
In the past I would say “okay, let me get started with “public void test_import”.
That’s a real shame, because the user just gave me the test cases I should be implementing, and I’m ignoring it!
Instead, I take those “requirements”, replace spaces with underscores, and there you go, a test method! That easy.
In the user controller test class I will have the following tests:
def test_should_be_able_to_open_the_import_dialog_from_the_main_page
# …
end
def test_should_be_able_to_select_a_local_file_from_the_import_dialog
# …
end
def test_should_see_the_contents_of_the_imported_file_applied_to_his_page
# …
end
Whoa! 3 tests that are very explicit about what the behavior of the system is. Everyone wins. The customer can be sure I’m working on the right stuff, and I can be sure I’m not missing anything mysterious, like importing from the command line instead of importing from a dialog.
No need for extra documentation. The tests really are the documentation!
Of course, not all customers enjoy having to read code so they understand what’s going on. To make things cooler, there’s this rails rake task, “doc:agiledox” that parses your test files, and spits out a simple but accurate document, in plain english, for what the system should do.
In the example above, the document would say something like:
User should
- be able to open the import dialog from the main page
- be able to select a local file and import it
- see the contents of his file applied on the screen
Is this beautiful or what? Doesn’t that beat telling the user “our test_import is implemented and it passes” or writing word documents for them to throw away because they’re not accurate anyway?
And that’s assuming there are any tests in the first place. Which is far from being guaranteed.
Despite being test infected for the past 8 years or so, most of the projects I was involved with since then, have had a pretty poor coverage record. Somewhere between 25% and 45% … yawn.
Which brings me to the other thing I wanted to share. Hardcore test coverage.
2) I have to admit how impressed I was (am) with how easy rails makes testing web apps. At previous jobs it was sometimes hard to convince people that unit tests should test units. There was this tendency to test an implemented feature, automate it, and call it a unit test. In part because it’s hard to automate integration tests of course.
And those were the smart ones. Some believe that testing is what QA is supposed to do, not devs. But I digress.
rails goes much further, where integration tests are as easy to write as unit tests (very).
Because of that, our test coverage has been anywhere between 88% and 98%. Very nice, I like. But really, 98%? What’s up with the extra 2%? Some people wanted me to believe that the missing 2% were impossible to test.
Ah! Impossible? That sounds like a challenge!
I sat down last night, with the help of Nick Cave in the background, and I’m happy to report a perfect score. 100% test coverage. Every single line of code has at least a test exercising it.
This is what it looks like:
–
So the lessons are:
1- use the word “should” for your tests. Every time you’re going to implement something, start by saying “the system/user/administrator/etc should blah blah blah”. If you can’t put it in those terms, you don’t know what you are doing. If you can, you have your test cases right there.
2- I’m sure there are things that are impossible to write automated tests for. I haven’t seen it yet though.