Re: [Cppunit-devel] A new architecture ?
Brought to you by:
blep
From: Baptiste L. <bl...@cl...> - 2001-11-04 12:36:27
|
----- Original Message ----- From: "Steve M. Robbins" <ste...@vi...> To: "CppUnit Development Mailing List" <cpp...@li...> Sent: Monday, October 22, 2001 5:02 AM Subject: [Cppunit-devel] A new architecture ? [...] > Test Construction Classes (CppUnit::Test) > ----------------------------------------- > > I'll leave aside the assertions and the exception classes for > the moment. > > The current design includes single test cases and suites of test > cases, both of which implement a common interface. As Baptiste > pointed out these are, respectively, the leaf, composite, and > component classes of a "Composite Pattern". I propose to have three > classes that serve the same purposes, all in CppUnit::Test > namespace: > > Base - a pure virtual class that defines the interface (i.e. the Component) > Case - the leaf class that implements a *single* test case; subclass of Base > Suite - the composite component (also a subclass of Base), consisting > of a set of tests where a test could be any subclass of Base > > To these basic three -- each of which are fairly simple and clean -- \ > I also added the following. > > CaseWithException: a single test that requires that a specified > exception is thrown. The class is templated on the exception > type. How does CaseWithException relates to MethodAdpator ? > FunctionAdaptor: takes a pointer to a "void foo(void)" function > and turns it into a CppUnit::Test::Case object. Why did you add that factory ? Have you found a use for implementing test case as global function ? > MethodAdaptor: takes a pointer to a "void foo(void)" class method > and turns it into a CppUnit::Test::Case object. > > > These three are all subclasses of CppUnit::Test::Case, so very little > extra code is required. > > > Using the MethodAdaptor and CaseWithException, I can easily implement > the CPPUNIT_TEST_SUITE(), CPPUNIT_TEST(), CPPUNIT_TEST_EXCEPTION(), > and CPPUNIT_TEST_SUITE_END() macros. The beauty of this scheme is What happen to CPPUNIT_TEST_SUB_SUITE ? > that *any* class can contain a test suite. In the current scheme, I > think that you can only use these macros in a subclass of > CppUnit::TestCase. And with the FunctionAdaptor you can make test > suites out of arbitrary global functions. > So the test-definition side seems sufficiently flexible, at least > to me :-). Have I missed something out? The current macro implementation depends on a common base class to implement the test hierarchy feature (CPPUNIT_TEST_SUB_SUITE). A factory is used to instantiate the correct class at each level of the hiearchy (in registerTests() ). The factory returns a CppUnit::Test * which is the source of the current dependency to TestCase. Beyond adding CaseWithException, I don't see what is different to the current architecture. Could you point it out ? How did you implement the CaseWithException ? I remember trying implementing CPPUNIT_TEST_EXCEPTION that way (using a test decorator that expected a specific exception), but it would have been a TestCase decorator, and it needed to access doRun() on the decorated test case, which is protected. So I fell back on a template member function added to TestSuiteBuilder (yuk!) > Test Result Classes (CppUnit::Result) > ------------------------------------- > > To keep the code clean and flexible, I opted here to use the composite > pattern again. I used CppUnit::Result::Base for the interface class, > > class Base > { > public: > virtual ~Base() {}; > > file://! Callback to indicate a test is about to start. > virtual void startTest( CppUnit::Test::Base* test ) = 0; > > file://! Callback to indicate that a failure has occurred. > virtual void addFailure( CppUnit::Failure* failure ) = 0; > > file://! Callback to indicate a test has just ended. > virtual void endTest( CppUnit::Test::Base* test ) = 0; > }; > > and CppUnit::Result::Group for the composite class. The latter is > basically identical to the test suite class in that it contains > a vector of pointers to result classes, and the callbacks are > passed along to each result object in the group. This look goods. Though, why change the class names ? I find that TestListener more intention revealing... Also, I think that the composite should probably be embeded in one of the CppUnit component. I think that in most cases, you use both a progress (when the test start to take some time, you want to know if you run into a infinite loop) and a result (even if only wasSucessful() ). > What's Next > ----------- > > I believe that the current functionality is available using the simple > classes outlined above. Those, plus the current assertion functions, > exception class, and test registry classes. I haven't looked too > carefully at the latter, however; perhaps there is room to simplify > those also. > > There are two questions to consider. First, is it worth reworking the > foundations of CppUnit? Certainly, the gain in clarity is persuasive > to me; how about the rest of you? Second, if we decide to go ahead > with this, how much compability must be maintained? I beleive we should continue refactoring CppUnit. Beyond clarity, there is extensibility. By removing the "accumulator" of result test from TestResult, it is easier to change or extend both the "accumulator" and TestResult. Concerning compatibility, we should consider the part of CppUnit a user interact with frequently. For myself: - writing a new test case : helper macros - using assertion : CPPUNIT_ASSERT* - writing a new assertion : Asserter - displaying progress : TestListener - printing result : TestResult and misc. outputter The helper macros are fairly independent from CppUnit. Change might concern base class or setUp()/tearDown() though. Asserter has been designed to be a facade to CppUnit assertion implementation, so there should be little impact for user concerning that class. TestListener and TestResult are still the dark ship of the lot, though your proposed change, should help reduce impact. If you look at the "main" interface of CppUnit, you can see that they are mainly macro and facade (with the exception of result). Maintaining compatibility for those should not be too difficult. [...] > My initial ambition was to reimplement completely the current Test, > TestCase, TestCaller, TestResult classes using the new classes. I > think that is a lot work and I'm not sure whether the gain is worth > it. It is straightforward to replace the TEST_SUITE macros with > equivalent ones using the new classes. So any tests that use the > macros alone will continue to function. What other techniques are in > use? Derivation of TestCase? Direct use of TestCaller and TestCase > subclass methods? How many are worth supporting? I once derived TestCase to implement some functionnal tests (creating TestCase from a file). Should not be of concern (used once compared to hundred of UT). From experience, using TestCaller directly is a source of trouble (you have to give the method name twice, and you may run into portability issue (remember when I forgot to put the '&' before the method name?)). The only use I can see is if you need to extend the current set of macros. Baptiste. --- Baptiste Lepilleur <gai...@fr...> http://gaiacrtn.free.fr/index.html Author of The Text Reformatter, a tool for fanfiction readers and writers. Language: English, French (Well, I'm French). |