Testing in Rails: Introduction

For some time, I’ve been wanting to teach how to get started writing tests for Ruby on Rails. Eventually I’d like to do a video training on the subject, but until then I’ve decided to write a series of blog posts as a tutorial.
Everyone agrees that testing is important, yet a small percentage of developers actually write tests. That’s understandable because there is a lot to learn when first starting with Ruby on Rails: installation, Ruby, the MVC framework, Rails, ActiveRecord, migrations, validations, routes, pagination, plug-ins, deployment… There’s more than enough to keep beginners busy during their first few projects. Testing requires learning yet another skill for something that seems like it can be put off until later. After all, you just want to get the site built, right?
Experienced developers often preach to beginners how important it is to test. I’ve even heard it said: “You are not a professional developer unless you write tests.” That is simply not true. You can write great applications, have lots of clients and make lots of money without writing tests. There are thousands of professional developers who don’t. But what I think they really mean by that statement is: Once you start writing tests, you’ll reap a lot of benefits (saving time and writing better code), take your programming to another level, and have a competitive edge over developers who don’t write tests. And who doesn’t want to save time and write better code???
But how do you get there? If you are a Ruby on Rails beginner, focus on the essentials first. Write an application or two. You can start writing tests from the beginning, but I think it’s best if you learn to program in Ruby and have a solid understanding of what Rails is doing first. After you have the fundamentals down, you’ll be ready to learn to write tests. Don’t worry, it’s not as difficult as it might seem. I’m going to walk you through the process step-by-step. (It’ll be an ongoing project spread out over many blog posts.)
What is Testing?
The good news is you are already testing your applications! When you write a controller action, design a view for it, then load it up in your web browser to see if you got the results you expected—that’s testing! When you fill out a form, submit it to your application, then go to the database to make sure everything was successfully stored—that’s testing! Dozens of times a day, you poke and prod at your application to make sure your code does what it should. If it doesn’t, you change the code and test it again.
How many times have you reloaded pages while building an application? How many times have you filled out forms with fake data? I’ve done it thousands of times. (Hundreds of thousands?) It’s a repetitive task that is essential when web programming. You know what is really good at repetitive tasks? Computers. They love them. Live for them even. And computers are much faster than you. So why not let your computer do your testing for you?
That’s the idea behind writing tests. Instead of submitting a web form to test your model’s validations are working, you can write a bit of code (in Ruby) that will test it for you. Instead of clicking on a link to make sure that you get redirected to the expected page, you can write a test that will “click the link” for you and “see” if the next page is what was expected. Once you take the time to write tests, you can ask the computer to run all of your hundreds of tests, or all of your thousands of tests, with a single command. You’ll get the results in seconds. If something goes wrong, it will tell you which test failed. You’ll make a few changes to your code and run the tests again. In less time than it takes to load it up in browser and test it yourself, you’ll know if it worked and you’ll know if you broke something else inadvertently in the process.
Testing is not the same as debugging. Testing can be an important tool when debugging by revealing problem areas, but you shouldn’t confuse the two. Testing confirms that software is doing the correct jobs and doing those jobs correctly. Debugging is tracking down the code that needs to change so that it does the correct jobs correctly.
The Benefits of Testing
It is worth reviewing the benefits of testing before diving in. Once you realize how helpful it can be, it should help make testing an indispensible habit.
Time spent testing: The most obvious benefit of writing tests is that you’ll save time clicking links, filling out forms, checking the database, etc. The computer will do it for you, and much faster. Ask yourself how many hours you spend testing by hand?
More accurate testing: Your computer will also do a better job of testing (assuming you provide it with good tests of course). You are more likely to make mistakes— to fill out a form field correctly, to forget to clear a record from the database, to make faulty assumptions. Removing those opportunities for human error will save you development time. I can’t count the number of times I have wracked my brain over why something wasn’t working only to discover that I had made a mistake in my manual testing.
Better tests: Writing tests will produce better tests. For example, the tests you write can try dozens of possible values while if you tested it manually you might only try a few. If you need to make sure a value can be any number between 1 and 100, you might try 0, 1, 37, 99, 100, and 1000 and then call it done. But it’s easy to write a test that will loop and test every single value. Writing tests also gives you the opportunity to think about how to push your application to its limits. You can write tests that torture your application in every way that you can imagine, before users have a chance to torture it.
Better code: The act of writing tests will force you to reconsider the assumptions that you had when you were in the “let’s just get it working” frame of mind. It forces you to review each part of your code with a critical eye. In the previous example, the act of writing tests might make you wonder how your application would behave if the value were -1, “1″ or [1], cases you might not stop to consider otherwise. For me, it is also an opportunity to take another pass at cleaning up the code, adding comments, or notes about future improvements I want to remember to make.
Confidence: You’ll know with confidence which processes work and which ones don’t. That’s no small thing! You can deliver a project to a client or make an application available to the world and not lose sleep wondering if there are scenarios that you didn’t try. Sure, you’ll find bugs that you didn’t consider and for which you didn’t write test cases. But once the bugs are brought to your attention, you can write a test case, fix the bug and know with confidence that the bug is gone for good.
Confident refactoring: Having good tests will also give you confidence when refactoring your code. If all your tests pass at the beginning of the day then they should still pass at the end of your refactoring. You’ll know if you broke something and you’ll have a good indication as to where to start looking for the problem. That will save you a lot of time and headaches.
Confident upgrading: Software versions are going to change. Ruby, Rails, database software, plug-ins and third party code. If you have a good test suite that you can run on your development server after installing the new version, you’ll know in seconds if something stopped working. Then you’ll be able to upgrade your production server with confidence. (Because Rails will be moving to version 2.0 soon, it makes this a great time to get your test code together!) Tests can take the pain and doubt out of upgrading.
The Costs of Testing
Okay, okay… I’ve hopefully sold you on the usefulness of testing by now. But what are the downsides? I can think of three.
Learning tests: It will take time to learn how to write tests (especially good tests). I’ll try to make learning tests as easy as I can. We’ll start by using what you already know about Ruby and Rails so you won’t have to learn everything about testing before you can get started.
Making time to write tests: There’s no two ways about it, writing tests takes time and discipline. But if you weigh the time spent writing tests against the time you spend manually testing, tracking down bugs, and worrying that your application is delicate, I’m confident you’ll see that writing tests saves time. Having good discipline is another matter. Maybe you’ll write your tests simultaneously with your code. Maybe you’ll write them at the end of the day, at the end of the week, or at the end of a section of code. (And eventually I’ll talk about writing tests even before you start coding.) You’ll have to find the style and rhythm that works for you. My only advice is: when you find yourself falling back to manually testing, ask yourself if you wouldn’t be better off stopping to write a few tests instead.
Imperfect tests: It is possible, especially early on, that your tests might not be thorough enough. Tests that don’t rigorously test every aspect of your application only give you a false sense of security. (There’s a programmer joke that goes: “Q: What parts of my application do I need to test? A: Only the ones that need to work.”) I’ll try to help show you what to test and how to test it, but it will be up to you to make sure you’ve covered all your bases. Like everything, experience will make you better. Always test everything and be paranoid.
Getting Ready
Over the next few posts, I’ll cover how to write unit tests, first in Ruby and then in Rails, and I’ll show you how to use fixtures to store sample data. Hopefully, I’ll be able to demystify testing so that you’ll see that it’s not a big leap from the Ruby on Rails skills you already have.
I have one recommendation before you start learning tests. Get comfortable working with the Rails console. The console is a great way to quickly instantiate objects, change values and see if you get the results you expected. It’s one of the best ways to manually test your application! For example, try something like this for one of your applications on your development server.
ruby script/console
# script/console defaults to your development environment
order = Order.find(1)
orig_li_array = order.line_items.collect {|li| li.id }
new_li = LineItem.new(:product_id => 100, :quantity => 1, :price => 300)
order.line_items << new_li
new_li_array = order.line_items.collect {|li| li.id }
orig_li_array.length
new_li_array.length
Try out your model’s class and instance methods. Create and save an object that is missing a value required by validates_presence_of to test if your validation is working. Try deleting an object that has a has_many … :dependent => :destroy relationship and then see if the expected records were deleted.
The console can be a powerful tool on it’s own, but having experience manually testing your application from the console is going to make writing good tests easy.
UPDATE: The series continues in Part 1.

November 13th, 2007 at 11:31 pm
[...] Skoglund has a very good start on a series of articles on Testing in Rails. It’s only an introduction at this point, but it’s very well put together and I [...]
January 12th, 2008 at 5:04 pm
[...] can be found in the Rails manual and Kevin Skoglund at Null is Love is currently in the middle of a series of posts on Rails testing. Hopefully my sample code coupled with these links will be [...]
January 15th, 2008 at 5:56 am
Dear Kevin
I really like your tutor ruby on rails.i did php and mysql course at one institution and i don’t think fine to put the name of that institution there but from that i got a big lesson in future think before pay, as i pay lots of fees but in return i get nothing because the Instructor are not sharing there whole knowledge.they only teach limited to the course. but when i got your tutor i learned a lot really so i think spending money on tutor like your is much much benefit as compare to study in some bad institution. i encourage you and lynda.com to produce more more project like this to the world. i really appreciate you and your team for making those tutor. i don’t see any donation button here, if there is any donation box i happily contribute some for your further project.
thanks. lynda.com
January 15th, 2008 at 10:49 am
Tashi, thank you for the kind words. No donations necessary–just let other people looking for help know about the training. I will have more training coming in 2008.
January 15th, 2008 at 9:10 pm
[...] Driven Development (BDD) with rspec in Ruby, based off the examples provided by this excellent Null Is Love series of posts. The only thing I know externally, in terms of BDD, is the process [...]