{"id":1997,"date":"2009-08-31T06:59:46","date_gmt":"2009-08-31T14:59:46","guid":{"rendered":"https:\/\/www.ultrasaurus.com\/?p=1997"},"modified":"2009-08-31T06:59:46","modified_gmt":"2009-08-31T14:59:46","slug":"ruby-unit-test-frameworks","status":"publish","type":"post","link":"https:\/\/www.ultrasaurus.com\/2009\/08\/ruby-unit-test-frameworks\/","title":{"rendered":"ruby unit test frameworks"},"content":{"rendered":"

In preparation for teaching Ruby in a class with test first teaching<\/a>. I decided to evaluate a few test frameworks. I thought initially to use Test::Unit, since it seemed easy to understand and ships with Rails. Wolfram Arnold argued that Test::Unit would burden the new folks with legacy<\/a>. Alex Chaffee also advocated RSpec<\/a>, but other<\/a> friends<\/a> from the Twittervese had good things to say about shoulda. Some folks declared it to be simply a matter of taste<\/a>.<\/p>\n

Even so, I wanted to make an informed decision and refine my palette for Ruby tools, so I wrote a simple exercise in each of Test::Unit, Shoulda and RSpec.<\/p>\n

Test::Unit<\/h2>\n
\nrequire 'test\/unit'\nrequire 'pig_latin'\n \nclass PigLatinTest < Test::Unit::TestCase\n    include PigLatinTranslator\n \n    def test_simple_word\n        s = translate("nix")\n        assert_equal("ixnay", s)\n    end\n \n    def test_word_beginning_with_vowel\n        s = translate("apple")\n        assert_equal("appleay", s)\n    end\n \n    def test_two_consonant_word\n        s = translate("stupid")\n        assert_equal("upidstay", s)\n    end\nend\n<\/pre>\n

With the above code saved as “test_pig_latin.rb” you run it by simply executing it with Ruby.<\/p>\n

$ ruby test_pig_latin.rb\nLoaded suite test_pig_latin\nStarted\nFFF\nFinished in 0.01091 seconds.\n\n  1) Failure:\ntest_simple_word(PigLatinTest) [test_pig_latin.rb:9]:\n expected but was\n.\n\n  2) Failure:\ntest_two_consonant_word(PigLatinTest) [test_pig_latin.rb:19]:\n expected but was\n.\n\n  3) Failure:\ntest_word_beginning_with_vowel(PigLatinTest) [test_pig_latin.rb:14]:\n expected but was\n.\n\n3 tests, 3 assertions, 3 failures, 0 errors\n<\/pre>\n
\n

Shoulda<\/h2>\n

Notice in the code below that Shoulda is simply and extension to Test::Unit. The PigLatinTest also subclasses Test::Unit::TestCase, just as the example above; however, the code inside the test case looks substantially different (and more readable in my opinion). You can actually mix Shoulda tests (below) with regular TestCase test methods (above) in the same TestCase. This is an advantage to Shoulda over RSpec if you have a codebase that already has lots of unit tests; however, I have also used RSpec and Test::Unit in the same project (you just have to remember to ‘rake test’ and ‘rake spec’).<\/p>\n

require 'rubygems'\nrequire 'shoulda'\nrequire 'pig_latin'\n\nclass PigLatinTest < Test::Unit::TestCase\n  include PigLatinTranslator\n\n  context \"#translate\" do\n\n    should \"translate a simple word: nix\" do\n      s = translate(\"nix\")\n      assert_equal(\"ixnay\", s)\n    end\n\n    should \"translate a word beginning with a vowel: apple\" do\n      s = translate(\"apple\")\n      assert_equal(\"appleay\", s)\n    end\n\n    should \"translate a two consonent word: stupid\" do\n      s = translate(\"stupid\")\n      assert_equal(\"upidstay\", s)\n    end\n\n  end\nend<\/pre>\n

With the code above saved as “test_shoulda_pig_latin.rb” you use the same process as above by just executing the file with ruby.<\/p>\n

$ ruby test_shoulda_pig_latin.rb\nLoaded suite test_shoulda_pig_latin\nStarted\nFFF\nFinished in 0.008268 seconds.\n\n 1) Failure:\ntest: #translate should translate a simple word. (PigLatinTest)\n [test_shoulda_pig_latin.rb:12:in `__bind_1251676444_52936'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `call'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `test: #translate should translate a simple word. ']:\n<\"ixnay\"> expected but was\n<\"translation\">.\n\n 2) Failure:\ntest: #translate should translate a two consonent word. (PigLatinTest)\n [test_shoulda_pig_latin.rb:22:in `__bind_1251676444_58860'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `call'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `test: #translate should translate a two consonent word. ']:\n<\"upidstay\"> expected but was\n<\"translation\">.\n\n 3) Failure:\ntest: #translate should translate a word beginning with a vowel. (PigLatinTest)\n [test_shoulda_pig_latin.rb:17:in `__bind_1251676444_59935'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `call'\n \/Library\/Ruby\/Gems\/1.8\/gems\/thoughtbot-shoulda-2.10.2\/lib\/shoulda\/context.rb:351:in `test: #translate should translate a word beginning with a vowel. ']:\n<\"appleay\"> expected but was\n<\"translation\">.\n\n3 tests, 3 assertions, 3 failures, 0 errors<\/pre>\n
\n

RSpec<\/h2>\n
require \"pig_latin\"\n\ndescribe \"#translate\" do\n  include PigLatinTranslator\n\n  it \"should translate a simple word\" do\n    s = translate(\"nix\")\n    s.should == \"ixnay\"\n  end\n\n  it \"should translate a word beginning with a vowel\" do\n    pending\n    s = translate(\"apple\")\n    s.should == \"appleay\"\n  end\n\n  it \"should translate a two consonent word: stupid\" do\n    pending\n    s = translate(\"stupid\")\n    s.should == \"upidstay\"\n  end\n\nend<\/pre>\n

The code above is saved in a file called “pig_latin_spec.rb” and run it using the ‘spec’ command. You will need to have installed the rspec gem (sudo gem install rspec).<\/p>\n

$ spec pig_latin_spec.rb\nF**\n\nPending:\n\n#translate should translate a word beginning with a vowel (TODO)\n.\/pig_latin_spec.rb:11\n\n#translate should translate a two consonent word: stupid (TODO)\n.\/pig_latin_spec.rb:17\n\n1)\n'#translate should translate a simple word' FAILED\nexpected: \"ixnay\",\n     got: \"translation\" (using ==)\n.\/pig_latin_spec.rb:8:\n\nFinished in 0.035728 seconds\n\n3 examples, 1 failure, 2 pending<\/pre>\n

Conclusion<\/h2>\n

I like RSpec best since I find the output to be most readable. I love the pending keyword, which allows me to set up the tests as an exercise for the class with only one test failing. I find it helps focus on exactly one test and one failure. I considered going with Shoulda because the tests are just as readable as RSpec, even if the output takes some learning to read, because of my initial thought that Test::Unit held less magic. However, on closer inspection, I realized that Test::Unit has one significant magical incantation: you merely declare a class and when that class is defined, it runs the test. This seemed not the kind of topic I would want to teach in an intro class. Even some experienced programmers might struggle with understanding the mechanism that allows such a construct to function. I concluded that all of the test frameworks require serious magic, and picked RSpec since I found it to be most usable for test writing and analysis of the output.<\/p>\n

Caveat: this exercise was for pure Ruby. In Rails, I wonder if Shoulda tests would be more concise, making them easier to write and read and, therefore, making it worth the steeper learning curve on reading the output. <\/p>\n","protected":false},"excerpt":{"rendered":"

In preparation for teaching Ruby in a class with test first teaching. I decided to evaluate a few test frameworks. I thought initially to use Test::Unit, since it seemed easy to understand and ships with Rails. Wolfram Arnold argued that Test::Unit would burden the new folks with legacy. Alex Chaffee also advocated RSpec, but other… Continue reading →<\/span><\/a><\/p>\n","protected":false},"author":84,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/posts\/1997"}],"collection":[{"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/users\/84"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/comments?post=1997"}],"version-history":[{"count":0,"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/posts\/1997\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/media?parent=1997"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/categories?post=1997"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ultrasaurus.com\/wp-json\/wp\/v2\/tags?post=1997"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}