login

UsingTestUnit (Ruby)

HomePage | RecentChanges | Preferences | Wikis | RubyGarden | Feed-icon-16x16

This is a transcription of an email sent by NathanielTalbott explaining how he uses TestUnit.


First of all, I don't usually start with the code. Usually, I start with the test. What do we want to do? How about a simple little class that will wrap a string at some column width? The test seems simple enough:

tc_string_wrapper.rb

 require 'test/unit'
 class TC_StringWrapper < Test::Unit::TestCase
   def test_wrap
     wrapper = StringWrapper.new
     assert_equal("This is a\nwrapped\nline.",
                  wrapper.wrap("This is a wrapped line.", 9),
                  "The line should have been wrapped to 9 columns")
   end
 end
Things to note: first of all, you need to require 'test/unit'. Not only does this get you the whole framework, it will also magically run your tests for you when you give the file to the interpreter. Secondly, you need a subclass of Test::Unit::TestCase? to inherit all the framework goodness. Third, you need an instance method within your TestCase? that starts with "test". Every such method will be run as an individual test when the file is invoked. Finally, the real power is in the various "assert" methods, such as the #assert_equal used here. To test something, we assert that some statement is correct. If it isn't, we get a big fat failure. If it is, then we know we're done.

OK, so now we have a test. Let's run it:

Run results

 C:\projects-ruby\Sandbox>ruby tc_string_wrapper.rb
 Loaded suite tc_string_wrapper
 Started...
 ..
 Error occurred in test_wrap(TC_StringWrapper): NameError: uninitialized
 constant StringWrapper at TC
 _StringWrapper
        tc_string_wrapper.rb:5:in `test_wrap'
        tc_string_wrapper.rb:4
 Finished in 0.0 seconds.
 1 runs, 0 assertions, 0 failures, 1 errors
Well, waddaya know, I didn't create StringWrapper? yet. Notice that the framework caught the exception for me and gave me some nice information about it. It's handy to not have an unexpected exception (is that redundant?) bomb the interpreter and skip all the other tests.

This of course poses no real problem; here's something that will eliminate the error (actually two of them; normally I'd probably slay them one at a time, but I don't want this to drag on for too long):

string_wrapper.rb

 class StringWrapper
    def wrap(string, columns)
    end
 end
Let me add the require to the test and see if I can get a real failure now:

tc_string_wrapper.rb

 require 'test/unit'
 require 'string_wrapper'
 class TC_StringWrapper < Test::Unit::TestCase
    def test_wrap
       wrapper = StringWrapper.new
       assert_equal("This is a\nwrapped\nline.",
                    wrapper.wrap("This is a wrapped line.", 9),
                    "The line should have been wrapped to 9 columns")
    end
 end

Run results

 C:\projects-ruby\Sandbox>ruby tc_string_wrapper.rb
 Loaded suite tc_string_wrapper
 Started...
 ..
 Failure occurred in test_wrap(TC_StringWrapper)
 [tc_string_wrapper.rb:7]: The line should have been
 wrapped to 9 columns. Expected <This is a
 wrapped
 line.> but was <nil>
 Finished in 0.0 seconds.
 1 runs, 1 assertions, 1 failures, 0 errors
Notice the helpful failure message. The string is not what was expected... as a matter of fact, it's not even a string. This is a possibility to make it pass:

string_wrapper.rb

 class StringWrapper
    def wrap(string, columns)
       string.scan(/(.{1,9}) /).join("\n")
    end
 end
I have no earthly idea if that will work (really!), but the tests are waiting to tell me:

Run results

 C:\projects-ruby\Sandbox>ruby tc_string_wrapper.rb
 Loaded suite tc_string_wrapper
 Started...
 ..
 Failure occurred in test_wrap(TC_StringWrapper)
 [tc_string_wrapper.rb:7]: The line should have been
 wrapped to 9 columns. Expected <This is a
 wrapped
 line.> but was <This is a
 wrapped>
 Finished in 0.0 seconds.
 1 runs, 1 assertions, 1 failures, 0 errors
Oh sorrow, oh tragedy, oh failure! I'm getting closer, but no enchilada. No surprise either... I write my tests before I write my code precisely because I'm not usually smart enough to get this stuff right the first time. Maybe the second?:

string_wrapper.rb

 class StringWrapper
    def wrap(string, columns)
       string.scan(/(.{1,9})(?: |$)/).join("\n")
    end
 end
Run results
 C:\projects-ruby\Sandbox>ruby tc_string_wrapper.rb
 Loaded suite tc_string_wrapper
 Started...
 ..
 Finished in 0.01 seconds.
 1 runs, 1 assertions, 0 failures, 0 errors
Yippee! In testing nomenclature (although it doesn't quite map to the console), I've got a green bar! All my tests pass. Of course, at this point, they're trivial and have lots of holes, but that just means I need more of them. If I were to continue, I'd keep adding things I thought might fail, fixing them, and passing my tests. I'd repeat this cycle until I was confident the method would stand up to my expectations of it.

This is really a simple process... copying the information in to the email took longer than writing and running both tests and code.

There's a lot more sugar in the framework (which you can read about at http://www.ruby-doc.org/stdlib/libdoc/test/unit/rdoc/index.html), but I've shared the basics of what I personally do with it. I could ramble on and on about testing (actually, I already have: http://rubycentral.org/2001/talks/testinginreverse/), but I don't want to overrun you with details. Let me know what questions you still have, or if I've totally misinterpreted your original question, and we'll go from there.

Others should feel free to correct me, chime in and/or make sense...


HomePage | RecentChanges | Preferences | Wikis | RubyGarden
Edit text of this page | View other revisions
Rev 17, Last edited at October 10, 2005 08:43 am by rgTomasPospisek / 194.150.244.67 (diff)
Find: