Rails Test Prescriptions
Keeping Your Application Healthy
Noel Rappin
Version: P1.0 (February 2011)
Copyright 2011, The Pragmatic Bookshelf. This book is licensed tothe individual who purchased it. We don't copy-protect itbecause that would limit your ability to use it for yourown purposes. Please don't break this trustdon't allow othersto use your copy of the book. Thanks.
Dave & Andy.
Table of Contents
Copyright 2011, The Pragmatic Bookshelf.
Part 1
Getting Started with Testing in Rails
Chapter 1
The Goals of Automated Developer Testing
A Testing Fable
Imagine two programmers working on the same task. Both are equally skilled, charming, and delightful people, motivated to do a high-quality job as quickly as possible. The task is not trivial but not wildly complex either; for the sake of discussion, well say its behavior based on a new user registering for a website and entering pertinent information.
The first developer, who well call Ernie, says, This is pretty easy, and Ive done it before. I dont need to write tests. And in five minutes Ernie has a working method ready to verify.
Our second developer is, of course, named Bert. Bert says, I need to write some tests. Bert starts writing a test, and in five minutes, he has a solid test of the new feature. Five minutes more, Bert also has a working method ready to verify. Because this is a fable, we are going to assume that Ernie is allergic to automated testing, while Bert is similarly averse to manually running against the app in the browser.
At this point, you no doubt expect me to say that even though it has taken Bert more time to write the method, Bert has written code that is more likely to be correct, robust, and easy to maintain. Thats true. But Im also going to say that theres a good chance Bert will be done before Ernie.
Observe our programmers a bit further. Ernie has a five-minute lead, but both people need to verify their work. Ernie needs to test in a browser; we said the task requires a user to log in. Lets say it takes Ernie one minute to set up the task and run the action in his development environment. Bert verifies by running the testthat takes about ten seconds. (Remember, Bert has to run only one test, not the entire suite.)
Lets say it takes each developer three tries to get it right. Since running the test is faster than verifying in the browser, Bert gains a little bit each try. After verifying the code three times, Bert is only two and half minutes behind Ernie.
At this point, with the task complete, both break for lunch (a burrito for Bert, an egg salad sandwich for Ernie, thanks for asking). After lunch, they start on the next task, which is a special case of the first task. Bert has most of his test setup in place, so writing the test only takes him two minutes. Still, its not looking good for Bert, even after another three rounds trying to get the code right. Hes still a solid two minutes behind Ernie.
Bear with me one more step, and well get to the punch line. Ernie and Bert are both conscientious programmers, and they want to clean their code up with a little refactoring. Now Ernie is in trouble. Each time he tries the refactoring, he has to spend two minutes verifying both tasks, but Berts test suite still takes only about ten seconds. After three more tries to get the refactoring right, Bert finishes the whole thing and checks it in three and a half minutes ahead of Ernie.
My story is obviously simplified, but lets talk a moment about what I didnt assume. I didnt assume that the actual time Bert spent on task was smaller, and I didnt assume that the tests would help Bert find errors more easilyalthough I think that would be true. The main point here is that its frequently faster to run multiple verifications of your code as an automated test than to always check manually. And that advantage is only going to increase as the code gets more complex.
There are many beneficial side effects of having accurate tests. Youll have better-designed code in which youll have more confidence. But the most important benefit is that if you do testing well, youll notice that your work goes faster. You may not see it at first, but at some point in a well-run test-driven project, youll notice fewer bugs and that the bugs that do exist are easier to find. Youll notice that its easier to add new features and easier to modify existing ones. As far as Im concerned, the only code-quality metric that has any validity is how easy it is over time to find bugs and add new behavior.
Of course, it doesnt always work out that way. The tests might have bugs. Environmental issues may mean things that work in a test environment wont work in a development environment. Code changes will break tests. Adding tests to already existing code is a pain. Like any other programming tool, there are a lot of ways to cause yourself pain with testing.
Who Are You?
The goal of this book is to show you how to apply a test-driven process as you build your Rails application. Ill show you whats available and try to give you some idea of what kind of tools are best used in what circumstances. Still, tools come and tools go, so what Im really hoping is that you come away from this book committed to the idea of writing better code through the small steps of a TDD or BDD process.
There are some things Im assuming about you.
Im assuming that you are already comfortable with Ruby and Rails and that you dont need this book to explain how to get started creating a Rails application in and of itself.
I am not assuming you have any particular familiarity with testing frameworks or testing tools used within Rails. If you do have familiarity, you may find some of the early chapters redundant. However, if you have tried to use test frameworks but got frustrated and didnt think they were effective, I recommend the Chapter , since they walk through the TDD process for a small piece of Rails functionality.
Over the course of this book, well go through the tools that are available for writing tests, and well talk about them with an eye toward making them useful in building your application. This is Rails, so naturally I have my own opinions, but all the tools have the same goal: to help you to write great applications that do great things and still catch the train home.
The Power of Testing First
The way to succeed with Test-Driven Development (TDD) is to trust the process. The classic process goes like this:
Create a test. The test should be short and test for one thing in your code. The result of the test should be deterministic.
Make sure the test fails. Verifying the test failure before you write code helps ensure that the test really does what you expect.
Write the simplest code that could possibly make the test pass. Dont worry about good code yet. Dont look ahead. Sometimes, just write enough code to clear the current error.
Refactor. After the test passes. Clean up duplication. Optimize. This is where design happens, so dont skip this. Remember to run the tests at the end to make sure you havent changed any behavior.
Repeat until done. This will, on paper at least, ensure that your code is always as simple as possible and always is completely covered by tests. Well spend most of the rest of this book talking about the details of step 1 and how to use Rails tools to write useful tests.
If you use this process, you will find that it changes the structure of the code you write. The simple fact that you are continually aligning your code to the tests results in code that is made up of small methods, each of which does one thing. These methods tend to be loosely coupled and have minimal side effects.