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 errorsNotice 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 errorsOh 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 errorsYippee! 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...