Steve Merrick [mailto:Steve.Merrick / Marconi.com] wrote: > Can someone explain to me the *details* of how to use > Test::Unit, preferably an example showing the source code to > be tested, the test code, and any other glue or details > necessary to make it all work? I've never done this before, > and it isn't clear to me how you arrange your code and > test-code so that your tests can be run automatically, > quickly and often. Test harnesses I've used in the past > require considerable effort to get started, and to use. TIA. Hmmm... big question. Let me see if I can give you a little piece of the answer to start chewing on, and then you can figure out what pieces are still missing. 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: <file name="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 </file> 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> 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 </run> Well, waddaya know, I didn't create StringWrapper yet. Notice that 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): <file name="string_wrapper.rb"> class StringWrapper def wrap(string, columns) end end </file> Let me add the require to the test and see if I can get a real failure now: <file name="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 </file> <run> 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 </run> 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: <file name="string_wrapper.rb"> class StringWrapper def wrap(string, columns) string.scan(/(.{1,9}) /).join("\n") end end </file> I have no earthly idea if that will work (really!), but the tests are waiting to tell me: <run> 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 </run> 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?: <file name="string_wrapper.rb"> class StringWrapper def wrap(string, columns) string.scan(/(.{1,9})(?: |$)/).join("\n") end end </file> <run> 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 </run> 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://testunit.talbott.ws/doc/), 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://www.rubyconf.com/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... Nathaniel <:((>< + - - | RoleModel Software, Inc. | EQUIP VI