I recently remarked that I’m beginning to enjoy writing tests, but—in the same breath—acknowledged that “enjoy” is not quite accurate. It’s more like I’ve moved from avoidance of testing to a neutrality about it, a mindset that sees its value and knows it’s attainable. That transition is an ongoing pursuit: of testing, of test-driven development, and of “enjoyment” of those things. An ongoing pursuit of happiness.
There are different types of tests:
- Unit and integration tests
- Model, view, and controller specs
- Feature and request specs
There are even differences of opinions about best practices (though, for RSpec, here are some practices that seem to be considered “better”). Acknowledging all of that diversity in test-driven development, my experience has been primarily contrained to RSpec, so this blog post will be too.
An example: testing Tender
One application I’ve built recently is Tender (code on Github and live deployed app). The key components to Tender boil down to the interface you see at right: (1) a User model and (2) the functionality of sending messages and bitcoin.
For that reason I decided on two key test areas:
Feature specs became my best friend. The most difficult part was mocking the omniauth login (so I’d have a valid test user who could “send” bitcoin). With this covered, I was able to run the code below.
1 2 3 4 5 6 7 8 9
And voilà, Capybara tests the feature perfectly. This was, of course, very easy to replicate to test form validations, requests, Bitcoin requests (instead of payments), etc.
Is this whole blog post about a shiny green badge on the Readme? Yes, in part. It looks so nice!
Jokes aside, it’s validating to have a seemingly comprehensive test suite. But it’s only validating because there’s value in an actually comprehensive test suite.
Testing is ultimately about making our jobs easier. As the RailsGuide on testing says:
- By simply running your Rails tests you can ensure your code adheres to the desired functionality even after some major code refactoring.
- Rails tests can also simulate browser requests and thus you can test your application’s response without having to test it through your browser.
Doesn’t that sound great? Ensuring adherence to the desired functionality even after refactoring. Knowing how your application will respond without opening your browser. Of course there are other ways of testing your app—you can open the browser and try it out. But how much more reliable, fast, and easy is it to have a test suite that you can run and trust?
This became evident over the course of building Tender. We’d have the functionality we wanted, then add a feature, which would break a different (previously working) feature, but we wouldn’t know that until hours or days later. Testing makes that feedback immediate, and ensures that your code is always working (or at least points out where it’s not working).
This was all great—I’m now more comfortable writing tests, and I see their value more clearly (ensuring my app is running is as simple as running
rspec or looking at my Travis build). That said, I’m certainly still learning. A helpful lesson came from John Kelly Ferguson, Flatiron alum and web developer extraordinaire who reviewed Tender’s code. His feedback is laid out nicely in this pull request.
What it boils down to is what Better Specs says on its homepage:
On the web there are many resources that give complete overview of _what_ you can do with RSpec. But there are fewer resources devoted to how to create a great RSpec test suite.
Better Specs tries to fill this gap by collecting most of the “best practices” that other developers learn through years of experience.
My learning has gotten me to an initial implementation of an RSpec test suite—I now look to Better Specs for some better (or even best) practices. That said, getting this far was an accomplishment in itself. Now to continue that pursuit of happiness.
Bonus: straight from the creator of Ruby on Rails
DHH just happened to post this essay the day after I wrote this blog. A helpful reminder to focus on what’s helpful—what makes programming easier—and not on dogmas of TDD or test-first.