Diary of a Rails rescue project, part 2: Testing
As mentioned previously, I’ve been spending spare cycles getting an outdated Rails application up to speed. Aside from the outdated versions of Rails and pretty much every gem used by the app, there’s a glaring problem: No usable tests. Without tests, I can’t be certain of what the application is supposed to do. What’s more, I can’t be sure that the rather massive changes I’m making to the code’s underpinnings won’t break functionality.
While the previous developer did have some tests in place, I quickly lost faith in them: On first run I got a number of deprecation warnings, then 60 percent of the provided tests failed. Upon further inspection, it became evident that most of the files in the spec/ directory were stubs generated by the likes of
rails generate scaffold.
To address this, I decided to write out my own high level tests of the application’s core functions. Building out these tests helped at several levels:
- I got a handle on what parts of the application were being touched by each given user interaction.
- I began making notes on potential design issues in the application.
- I had a happy path test to follow as my work progressed–in other words I could see that my changes weren’t detrimental to the application as a whole.
- I set the groundwork for a solid test suite for future developers to work with.
Before I could do that, though, I wiped out the existing spec/ directory and started fresh. Since the project is built on Ruby 1.8, and I don’t want to switch to a different version without tests in place, I had to look through various gems’ dependencies to find compatible versions, and adding them to my Gemfile:
group :development, :test do gem 'rspec-rails', '2.12.2' gem 'factory_girl_rails', '~> 1.7.0' gem 'capybara', '~> 1.1.4' end
With that in place I began writing request specs (now known as feature specs in Capybara 2.0 and onward). I started with something basic: What happens when a user logs in?
As it turns out, quite a bit. For starters, there were several attributes on the User model which appeared to have something to do with assigning roles. Doesn’t seem very DRY, so I made a note of it as something to possibly refactor later.
Next, I discovered that brand new users are required to fill out a profile. This information is stored separately. My tests were expanded to accommodate both paths–either a user logs in for the very first time and creates a profile, or the user already has a profile and proceeds accordingly.
Something else I noticed at this point: Some labels for UI elements were incongruous. For example, links and buttons related to signing in were labeled Sign In, but a user clicks Log Out to leave. In addition, the username field is actually the user’s email address. This should probably be labeled as such. More notes for things to look into later. With passing specs for logging in and out, I did some quick test refactoring–specifically, a macro to reuse login steps.
I decided to test what would happen when users enter incorrect login credentials, even though at first blush this should be handled by Devise. I was right, but in the process of running this test I uncovered a couple more options. The first allows new users to register; the second allows them to “retrieve” accounts from an earlier, non-Rails version of the application. Time for a few more tests.
As you might expect by now, testing registration left me with just as many questions as answers. For example, I noticed that registrations weren’t running through the same controller, model, etc. as other users. As it turns out, the registration process doesn’t create new users, it creates new requests. Why is this?
I also became curious if the account retrieval functionality was still necessary, or if everyone had been migrated to the new system now, a good five years after the original switch. Could this functionality be dropped?
And so on. As you can hopefully see by now, I learned quite a bit from a few hours of testing. These tests will also serve me well moving forward as I begin updating actual application code.
Takeaways and recommendations
- Write tests. Even if your application doesn’t have any tests, now’s as good a time as any to start writing them. Not sure how to get started? Allow me to shamelessly plug my book, Everyday Rails Testing with RSpec.
- Don’t take things for granted. As illustrated by my exploration of failed logins, it’s possible to discover things you otherwise wouldn’t have found.
- And again, take notes. You’re going to find a lot of issues big and small as you explore code with tests. Keep track of them to address down the road.
In the next post in this series, I’ll look at digging deeper into the application’s underpinnings, making them more future-proof in the process.
Everyday Rails Testing with RSpec: The book
If you liked my series on practical advice for adding reliable tests to your Rails apps, check out the new, expanded ebook version. Lots of additional, exclusive content and a complete sample Rails application. Get it now or learn more about the book.