From: "Berger, Daniel" Date: 2006-07-14T03:38:37+09:00 Subject: One class per test file (was [PATCH] --fqname option to test/unit/autorunner.rb) (Moved to ruby-talk from ruby-core since we're on a more general topic) > -----Original Message----- > From: mathew [mailto:meta@pobox.com] > Sent: Thursday, July 13, 2006 12:09 PM > To: ruby-core@ruby-lang.org > Subject: Re: [PATCH] --fqname option to test/unit/autorunner.rb > > > Daniel Berger wrote: > > I suspect the general consensus is that you shouldn't put > multiple > test case classes in the same file, > > Could you expand on why? I would say granularity, maintenance and mental clarity. By granularity, I mean the tests as a whole that are defined in your TestCase subclass should be able to stand on their own, i.e. the tests in one TestCase subclass should not depend on the results from another TestCase subclass. Likewise, the individual tests should be able to stand on their own. This rule can be broken in exceptional circumstances, e.g. socket programming tests, though even then the general philosophy is to uses mocks and/or stubs instead of relying on one test to fulfill a prereq for another test. I realize that's not always possible or easy, though. By maintenance I mean that it's usually more difficult to maintain a single test file when that file contains multiple subclasses of TestCase. Say, for example, that you have a Foo class and Bar class. Also say that you've created a TC_Bar and a TC_Foo class (both subclasses of TestCase), and they both have a method called "test_length", and put both of them and their tests in the same file. This creates two problems. First, using -n will be problematic. What if I want to run the test_length test? From which class will it run? Or will it run both? I don't actually know without looking, and I don't want to know. Second, what if you decide to change the Bar API down the road? Now you'll have to modify a file that contains test code completely unrelated to your changes. One final point on maintenance. When I see a list of files like tc_foo.rb, tc_bar.rb, etc, I can quickly figure out what each file is testing i.e. a class or module called 'foo' and 'bar', respectively. If someone later has to come along and maintain code that you've long abandonded, it's going to be more difficult for them to figure out where you're testing classes or modules, if at all. They'll have to manually inspect the code. I know that if I had to do that I would become very irritated very quickly. Lastly there's the issue of mental clarity. When I open up the test file I only want to mentally deal with ONE class or module. I don't want to worry about a bunch of code that's unrelated to the code that I want to test. Furthermore, if I'm taking over maintenance of someone else's code, I might start to wonder why the other subclasses of TestCase are in the same file. Are they related? Do the tests from one class impact another? Or did the original author just slap everything into a single file? (On a side note, in my own independant test suite for Ruby, I've broken the test cases down by *method* to keep it even more mentally manageable). > It never occurred to me that there > might be a > reason not to. I've been dividing up my test cases based on chunks of > functionality, not on implementation details. If you have classes with similar functionality being tested separately, then I would say you probably have a design issue (you should probably be using a mixin or subclass) or your tests are just not setup very well. Note that I say "probably" because I'm not entirely certain without seeing your test cases. Well, anyway, there you have it. Regards, Dan PS - I wonder if anyone proposed a "Philosphy of Unit Testing" at RubyConf this year. This communication is the property of Qwest and may contain confidential or privileged information. Unauthorized use of this communication is strictly prohibited and may be unlawful. If you have received this communication in error, please immediately notify the sender by reply e-mail and destroy all copies of the communication and any attachments.