Learn how to test using
Test::Unit instead of Rspec
A few weeks ago, I started working on a book about testing Rails apps written for those of us who struggled to understand what to actually test. The response was awesome. It showed me that I wasn’t the only one who wrestled with testing concepts. In the course of writing, I’ve been going back to the resources I used to learn up Rails in the first place to see where things went wrong. The answer is simple: I learned how to use Rspec instead of how to test
Trying to learn testing through Rspec is like trying to learn how to play baseball from a coach that only speaks Esperanto. Eventually, you’ll pick up the rules, but it’s going to be much easier if you don’t have to learn Esperanto first.
Testing is not an easy thing to pick up and understanding all the different facets of it can take some time. Test::Unit’s familiar class syntax coupled with assertions make testing click faster than with Rspec’s magical expectations. This is just like when we advise new Rails developers to avoid using scaffolding until they really understand what’s going on behind the scenes. Magic is awesome, but only when you are comfortable with what is going on behind the curtain.
To demonstrate what I’m talking about, let’s have a look at a test that makes sure a title is being stripped of special characters and downcased. Here’s the test written in Rspec:
describe "Article" do before(:each) do @article = Article.new end it "should create a lower-cased, hyphenated slug from the title" do @article.title = "Testing with Test::Unit" @article.slug.should_not match(/[^\w-]/) @article.slug.should_not match(/[A-Z]/) end end
And here’s the same test written in Test::Unit:
class ArticleTest < Test::Unit::TestCase def setup @article = Article.new end def test_slugging @article.title = "Testing with Test::Unit" assert_no_match /[^\w-]/, @article.slug, "Special characters were not replaced with hyphens" assert_no_match /[A-Z]/, @article.slug, "Capital letters were not down-cased" assert_equal @article.slug, "testing-with-test-unit" end end
Just from skimming over the tests, it’s obvious that Rspec is more readable. The Rspec DSL was designed for us humans to be able to understand what is happening at a glance. This is where Rspec gets my vote. Unfortunately, we aren’t talking about reading or sharing tests. We are talking about writing tests, and this is where Rspec has a heafty learning curve.
Let’s take a look at the two frameworks from a different, equally important angle: the documentation. The Rspec documentation is a bit rough. Before you can get started, you need to learn a pretty robust vocabulary in order to understand that you are looking to combine ‘expectations’ with ‘matchers’. Once you understand that, you still need to dig a bit through several different pages to understand the DSL. It’s a lot of extra work for someone who is still new to testing.
Test::Unit has the added benefit of not requiring a new Rails developer to go through additional configuration steps. It just works with Rails out of the box, and even creates the proper declarations when you call generators. Sure, you can get the same behavior out of Rspec, but we are aiming for as little friction for new testers as possible.
Rspec, Minitest::Spec, Shoulda, and other spec-based testing tools are great. I started writing ‘What do I Test?’ with Rspec as the framework of choice in mind. It’s well supported, removes a ton of friction in writing tests (if you know what you’re doing), and integrates with Rails swimmingly. What Rspec has done is fantastic, and you can’t go wrong with it once you have a handle on testing your Rails applications. I swapped the suggested first framework in the book from Rspec to Test::Unit not because it’s better, but because it’s easier to learn.