TestTemplate tutorial: creating a test base class

Building on the previous example, let us now assume a new user requirement mandates that the money laundering department must be notified when there's an account transfer with amount above 10 000.

To accommodate this, we have to have 2 test methods, one testing the case with amount below the limit and the other with amount above the limit.

The AccountTransferHandler will have a property of type LaunderingDepartment with corresponding getter and setter.

We start by creating AccountTransferTestBase class next to AccountTransferHandlerTest, extracting the anonymous descendant of the TestTemplate into it:

public class AccountTransferTestBase extends TestTemplate {
        ... the test data declared
        ... the three methods implemented
}

We also have to mock the LaunderingDepartment class and set the mock to the AccountTransferHandler created during setUp().

The accountTransfer method in AccountTransferHandlerTest now becomes accountTransfer_belowLimit:

@Test
public void accountTransfer_belowLimit() {
        new AccountTransferTestBase() {
        }.doTest();
}

Let's create a new accountTransfer_aboveLimit test method, starting from the below limit variant. We now have to add additional expectations, specific to the above limit test case.

@Test
public void accountTransfer_aboveLimit() {
        new AccountTransferTestBase() {
                @Override
                public Expectations getSpecificExpectations() throws Exception {
                        return new Expectations() {
                                {
                                        one(ld).notifyAccountTransfer(debit, credit, amount);
                                }
                        };
                }
        }.doTest();
}

The above limit test fails, because the notification is not called. We have to cater for the check to the amount. Let us extend the expectations in AccountTransferTestBase:

double amtEquivalent = 100;
...
public Expectations getExpectations() throws Exception {
        return new Expectations() {
                {
                        ...
                        one(amount).getBaseCurrrencyEquivalent();
                        will(returnValue(amtEquivalent));
                }

Now, in the above limit test method we override the afterSetUp() method to re-set the amtEquivalent to 10001.

@Override
public void afterSetUp() {
        amtEquivalent = 10001;
}

The tests now fail, because the getBaseCurrrencyEquivalent() is not called. After adding the call in the business method, only the above limit test fails. After we implement the business method properly, both the tests pass:

public void accountTransfer(Account debit, Account credit, Amount amount) {
        debit.debit(amount);
        credit.credit(amount);
        if (amount.getBaseCurrrencyEquivalent() > 10000) {
                getLaunderingDepartment().notifyAccountTransfer(debit, credit,
                                amount);
        }
}

Conclusion

Extracting the anonymous TestTemplate subclass into a named class enables easy and standardized sharing of the set up, expectations and business method invocation, which are common for multiple test cases. The individual test cases then re-use this common test-base class, furnishing it with test-specific set up and expectations.

This in turn:

  • prevents copy-paste in test code,
  • makes obvious the differences between the test cases,
  • makes obvious the shared behavior between the test cases.