How do you test?

It's all fine and dandy to know what unit testing is, but how do you actually do it? Isn't it really hard and complex? Good question! Let's see...

We need to write some code to wrap strings. Before we start writing the code, lets write a test to see if it works. Here's the test:


def testWrap
end
		

That's it! There's nothing more to it! All a test is is a method. Of course, ours doesn't actually check anything, but all you need for a test is a method in which to check something. Now, to keep things clean, we'll probably want to wrap the method in a class, like this:


class TC_Wrapper
	def testWrap
	end
end
		

Still pretty simple, eh? OK, lets actually do something in our test. We want to make sure that our wrapper wraps our string to a max of 10 characters per line:


class TC_Wrapper
	def testWrap
		wrapper = Wrapper.new
		actualString = wrapper.wrap("This is a long string")
		assert_equal("This is a\nlong\nstring")
	end
end
		

Note something very important: the test method is only three lines, and it already checks something very worthwhile! Also note that we haven't written a line of production code yet. So it would be nice if it actually ran, right? That only takes a few more lines of code:


require 'Lapidary/TestCase'

class TC_Wrapper < Lapidary::TestCase
	def testWrap
		wrapper = Wrapper.new
		actualString = wrapper.wrap("This is a long string")
		assert_equal("This is a\nlong\nstring")
	end
end

if __FILE__ == $0
	require 'Lapidary/UI/Console/TestRunner'
	Lapidary::UI::Console::TestRunner.run(TC_Wrapper)
end
		

OK, now we have a test that will run. Lets run it:

C:\rubyconf>ruby TC_Wrapper.rb
Loaded suite
Started...

Error occurred in testWrap(TC_Wrapper): NameError: uninitialized constant Wrapper at TC_Wrapper
        TC_Wrapper.rb:5:in `testWrap'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestCase.rb:58:in `send'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestCase.rb:58:in `run'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestCase.rb:57:in `catch'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestCase.rb:57:in `run'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestSuite.rb:36:in `run'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestSuite.rb:32:in `each'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/TestSuite.rb:32:in `run'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/UI/TestRunnerMediator.rb:63:in `runSuite'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/UI/Console/TestRunner.rb:65:in `startMediator'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/UI/Console/TestRunner.rb:49:in `start'
        C:/ruby/lib/ruby/site_ruby/1.7/Lapidary/UI/Console/TestRunner.rb:36:in `run'
        TC_Wrapper.rb:13
..
Finished in 0.0 seconds.
1 runs, 0 assertions, 0 failures, 1 errors
		

So the testing framework has caught an error due to our not having a Wrapper class yet. Lets define one, and see if it works:


class Wrapper
	def wrap(string)
	end
end
		

So now there's a Wrapper class, and we just need to require it in our test:


require 'Lapidary/TestCase'
require 'Wrapper'

class TC_Wrapper < Lapidary::TestCase
	def testWrap
		wrapper = Wrapper.new
		actualString = wrapper.wrap("This is a long string")
		assert_equal("This is a\nlong\nstring")
	end
end

if __FILE__ == $0
	require 'Lapidary/UI/Console/TestRunner'
	Lapidary::UI::Console::TestRunner.run(TC_Wrapper)
end
		

Here's the output now:

C:\rubyconf>ruby TC_Wrapper.rb
Loaded suite
Started...

Failure occurred in testWrap(TC_Wrapper) [TC_Wrapper.rb:8]: The string should have been wrapped. Exp
ected <This is a
long
string> but was <nil>
..
Finished in 0.0 seconds.
1 runs, 0 assertions, 1 failures, 0 errors
		

This output is, of course, correct, because our wrapper doesn't actually do anything yet. Lets add some wrapping code, and see what happens:


class Wrapper
	def wrap(string)
		words = string.split(/ /)
		line = nil
		lines = []
		words.each { | word |
			if (line.nil?)
				line = word
			elsif (line.length + word.length + 1 < 10)
				line = [line, word].compact.join(" ")
			else
				lines << line
				line = word
			end
		}
		lines << line
		lines.join("\n")
	end
end
		

Hmmm.... I wonder if that works.

C:\rubyconf>ruby TC_Wrapper.rb
Loaded suite
Started...
..
Finished in 0.0 seconds.
1 runs, 1 assertions, 0 failures, 0 errors
		

Cool! We've now written a test, and made it pass. In the actual live demonstration for this presentation, it took many more tries to get the code doing the right thing. However, each step of the way, it was incredibly simple to see if the code was working yet, because the tests always told us.

So now you know how to write a test, and it's time for my second assertion (working backwards, of course)...

Copyright (c) 2001 by Nathaniel Talbott. All Rights Reserved.