Re: [Cppunit-devel] class overview I: defining tests
Brought to you by:
blep
From: Baptiste L. <gai...@fr...> - 2001-07-10 13:24:50
|
Quoting "Steve M. Robbins" <ste...@vi...>: > class Test > ---------- > > CppUnit has a small hierarchy of classes rooted at class Test. Test > is a pure virtual class with no implementation code. I will assume it > is intended to be an interface class. By this I mean it exists only > to define the interface that a "test-like" classes must implement. If > we were writing Java, it would be an interface. Just to put it out since we haven't yet, Test/TestCase/TestSuite are respectively the Component/Leaf/Composite of a Composite pattern. > > The Test interface has one principal method: > > void run( TestResult* res ) > - run the test and collect the results in res Don't dismiss getName(), it is an essential element of "reporting" result. > class TestCase > -------------- > > The concrete class that represents a test should be TestCase. > In fact, however, TestCase is a framework to define one OR MORE tests. > The detailed description of TestCase says it defines the "fixture > to run multiple tests". > > The term "fixture" is bothering me. The documentation suggests that a > TestCase _is_ a fixture. If so, why have two terms? Does the word > "fixture" have a standardized meaning? > > TestCase is a subclass of Test, and implements run() as > > setUp() > runTest() > tearDown() > > According to the "cookbook", a TestCase object is intended as the > basic mechanism for defining a test. One should subclass TestCase > overriding runTest() appropriately. The code in runTest() would > typically make a number of assertions. I'm not sure what "fixture" means in english. The dictionnary did not provide anything that made sense. The "running" definition I have is that a fixture provides "context" and "facility" to run a test. I do not know of any standardized meaning, though it's also used in JUnit if I remember well. I'll continue the discussion about the TestCase/Fixture in TestCaller... > class TestCaller > ---------------- > > Here is where I really start getting muddled. > > A TestCaller is a subclass of TestCase that "provides access to a test > case method". Although nowhere clarified in the docs, "a test case > method" appears to refer to any member function of a TestCase that > takes no parameters and returns void. > > All through the TestCaller documentation, the term "fixture" is > used to mean "a TestCase instance". > > As near as I can tell, a TestCaller exists so that one can subclass a > TestCase, define a number of "void foo(void)" methods in it, and then > use a TestCaller to make an individual test case out of each such > method. > > I'm confused now as to whether an object of type TestCase should > be a single test case or a set of related test cases. The name > suggests it is a single test case. However, the TestCaller class > appears to promote the practice of defining a set of related tests > in a TestCase, and then generating a test case (TestCaller) for each. I finally get why you are so confused. Indeed you pointed out a weird stuff in the design. To write a unit test, you basically have two ways: - subclass TestCase, and override the runTest() method to implement your test. - have a class that define a setUp() and a tearOff() methods, and some tests methods. Then create a TestCaller to create a TestCase for each test method. As you can see, TestCaller does not really need a TestCase. As to why TestCase is used, you'll have to go down to the original CppUnit version. The fact are (concerning the use of TestCase to create test suite of TestCaller): - TestCase provides a base class, which define setUp() and tearOff() with empty implementation. - TestCase defines extraneous methods, such as getName(), runTest()... - TestCase is not use as a TestCase. Having a base class is a good thing (At least I think so, it make writing the macros much easier). Having the extraneous methods and a "wrong" use of TestCase is confusing. To solve that problem, I would suggest introducing a new base class: /*! This class represents an abstract fixture used to run test with TestCaller. * * A TestFixture provides a context and facilities to a set of TestCase * created with a TestCaller. * * setUp() is called to initialize the context before running a test, and * tearOff() to clean up after running a test. */ class TestFixture { public: virtual ~TestFixture() {} virtual void setUp() {} virtual void tearOff() {} }; Well, the definition need work, but the idea is there. The TestFixture is a place where you factor out common piece of code of many test case: object initialization, comparison... We would use the TestFixture class, just like we are using the TestCase class to define many tests: class MyTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( MyTest ); CPPUNIT_TEST( testSomething ); CPPUNIT_TEST( testSomethingElse ); CPPUNIT_TEST_SUITE_END(); public: void setUp(); void tearOff(); void testSomething(); void testSomethingElse(); }; Hope this help solve the confusing stuff. (Note that JUnit is even weirder in that aspect). > > In addition, the TestCaller class is able to ignore an "expected" > exception in its runTest() method. This is a useful facility! > Why is it not available in the base TestCase class? It is more than "ignoring": if the expected exception is not caught, the test failed. This means that we are implementing a test. I believe the base TestCase class should remain "test free" (provide facility to run and report result, but to not do any test). But you rise an interesting question: should the "exception" expected test be in a TestDecorator rather than in TestCaller ? This would also make the TestCaller "test free". > classes TestDecorator, RepeatedTest, TestSetUp > ---------------------------------------------- > > TestDecorator and RepeatedTest are straightforward. > > TestSetUp is a mystery, though. It appears very similar to > the TestCase class in that it wraps the Test::run() method > with virtual setUp() and tearDown() methods. But it doesn't > do any catching of exceptions like TestCase does. > What is the purpose of this class? To use delegation instead of subclassing to set a testing context. Here is an example of use: Problem: I need to set a global context each time I run the tests using the TestRunner. This could be initializing Thread Local Storage to set up the Database connexion, initializing resource such as COM... Solution: I provide a way for the user to "wrap" the test being run. The user could use TestSetUp to do that. Baptiste. --- Baptiste Lepilleur <gai...@fr...> http://gaiacrtn.free.fr/index.html Language: English, French |