This cookbook is related to the JUnit Cookbook by Gamma and Beck. I selected the same document structure to clearly show the similarities and the differences between these two frameworks.
The major differences of the two are
DDTUnit like JUnit tests do not require human judgment to interpret, and it is easy to run many of them at the same time. For testing requirements here is what you have to do:
public void initContext(){ initTestData("/DDT-TestResource.xml", "MyClassTest"); } public void testSimpleAdd() { Money m12CHF= (Money)getObject("m12CHF"); Money m14CHF= (Money)getObject("m14CHF"); Money result= m12CHF.add(m14CHF); // here is the JUnit assert way of testing Money expected= (Money)getObject("m26CHF"); assertTrue(expected.equals(result)); // here is part of DDTUnit way of testing addObjectToAssert("result", result); }
<?xml version="1.0"> <ddtunit> <cluster id="MyClassTest"> <group id="testSimpleAdd"> <test> <objs> <obj id="m12CHF" type="Money"> <amount>12</amount> <currency>CHF</currency></obj> <obj id="m14CHF" type="Money"> <amount>14</amount> <currency>CHF</currency></obj> </objs> <asserts> <assert id="result" type="Money" action="isEqual"> <amount>26</amount> <currency>CHF</currency></assert> </asserts> </test></group> </cluster> </ddtunit>
What if you have two or more tests that operate on the same or
similar sets of objects?
Tests need to run against the background of a known set of objects.
This set of objects is called a test fixture. When you are writing
tests you will often find that you spend more time writing the code
to set up the fixture than you do in actually testing values.
To some extent, you can make writing the fixture code easier by paying careful attention to the constructors you write. However, a much bigger savings comes from sharing fixture code. Often, you will be able to use the same fixture for several different tests. Each case will send slightly different messages or parameters to the fixture and will check for different results.
When you have a common fixture, here is what you do:
For example, to write several test cases that want to work with different combinations of 12 Swiss Francs, 14 Swiss Francs, and 28 US Dollars, first create a fixture:
public class MoneyTest extends DDTTestCase { private Money f12CHF; private Money f14CHF; private Money f28USD; public void initContext(){ initTestData("/DDT-TestResource.xml", "MyClassTest"); } protected void setUp() { f12CHF= (Money)getObject("f12CHF"); f14CHF= (Money)getObject("f14CHF"); f28USD= (Money)getObject("f28USD"); } }
<?xml version="1.0"> <ddtunit> <cluster id="MyClassTest"> <group id="MyMethod"> <objs> <obj id="f12CHF" type="Money"> <amount>12</amount> <currency>CHF</currency></obj> ... </objs> <group id="testSimpleAdd"> ... </ddtunit>
How do you write and invoke an individual test case when you have a
Fixture?
Writing a test case without a fixture is simple - just look at the example
above. Writing one with a Fixture is quit as easy.
For example, to test the addition of a Money and a MoneyBag, write:
Remember we allready defined a Fixture above.
public void testMoneyMoneyBag() { // [12 CHF] + [14 CHF] + [28 USD] == {[26 CHF][28 USD]} Money bag[]= { f26CHF, f28USD }; MoneyBag expected= new MoneyBag(bag); assertEquals(expected, f12CHF.add(f28USD.add(f14CHF))); }
new MoneyTest("testMoneyMoneyBag")
How do you run several tests at once?
As soon as you have two test methods, you'll want to run them together. You
could run the test methods one at a time yourself, but you would quickly grow
tired of that. Instead, DDTUnit as JUnit provides an object, TestSuite
which runs any number of test cases together.
For example, to run a single test case, you execute:
DDTTestResult result= (new MoneyTest("testMoneyMoneyBag")).run();
TestSuite suite= new TestSuite(); suite.addTest(new MoneyTest("testMoneyEquals")); suite.addTest(new MoneyTest("testSimpleAdd")); TestResult result= suite.run();
Another way is to let DDTUnit extract a suite from a DDTTestCase class. This is actually the same behavior as of JUnit. To do so you pass the class of your DDTTestCase to the TestSuite constructor.
TestSuite suite= new TestSuite(MoneyTest.class); TestResult result= suite.run();
TestSuites don't only have to contain TestCases. They contain any object that implements the Test interface. For example, you can create a TestSuite in your code and I can create one in mine, and we can run them together by creating a TestSuite that contains both:
TestSuite suite= new TestSuite(); suite.addTest(Joerg.suite()); suite.addTest(Kai.suite()); TestResult result= suite.run();
How do you run your tests and collect their results?
For now there is no extra implementation of a DDTTestRunner.
So you can use a TestRunner implementation provided by JUnit
or other packages like the Apache Ant
optional target <junit>.
These tools are used to define the suite to be run and to display its
results. You make your suite accessible to a TestRunner tool
with a static method suite() that returns a test suite like
described above.
For example, to make a MoneyTest suite available to a TestRunner, add the
following code to MoneyTest:
public static Test suite() { TestSuite suite= new TestSuite(); suite.addTest(new MoneyTest("testMoneyEquals")); suite.addTest(new MoneyTest("testSimpleAdd")); return suite; }