Time for a test

This is just a test

It’s Christmas! But the fun never stops here at 23Squared headquarters, it’s kind of like having Christmas every day of the year!

As promised, this blog is focused on testing…cue groans. Yes, developers hate testing, a fact well known to the entire world, let alone the development community. Testing is a pain, pure and simple. You’ve spent hours, no, days producing the most beautiful and eloquent piece of code ever written…even better, none of it came from Stackoverflow! You think it works perfectly only to find out that your tests prove it doesn’t. Yes, it’s a horrible feeling and yes, we’ve all been there. This is actually a good thing though, imagine how much more painful it would’ve have been if the bug hadn’t been noticed until it got into the development environment servers? What about pre-production? What about production? What you’ve actually been given is a get out of jail free card, a chance to tweak and finesse what you’ve just written until it actually is perfect.

So, you’ve written a test and it passes, great right? Well, perhaps not, not all tests are created equal. We believe that there should be at least as much consideration put into the structure of a test as the functional code itself. We aren’t necessarily talking about TDD here, more about quality test code that exercises as much of the functional code as possible. Tools like SonarQube are very helpful for making sure this is the case but they should be helping you catch the last little bits that have been missed; not driving the test code.

So, without further ado, we shall move on to the code. Consider the following (trivial) situation where we are creating a bank system represented by the following code:

public class Bank {

    private final int INITIAL_BALANCE = 0;
    private int balance;

    public Bank() {
        balance = INITIAL_BALANCE;
    }

    public void incrementBalance() throws IOException {
        balance+=10;
    }

    public void decrementBalance() throws IOException {
        balance-=10;
    }

    public int getBalance() {
        return balance;
    }
}

Not very exciting huh? Next, we have a service to play transactions to create a balance, the following method being the simplest possible incarnation:

public int playTransactionSetOne() {

    bank.incrementBalance();
    bank.incrementBalance();
    bank.incrementBalance();
    bank.decrementBalance();
    bank.incrementBalance();

    return bank.getBalance();
}

So, after this code runs we would expect the balance to be zero, we could test this with a trivial JUnit test via the following code:

 @Test
 public void shouldHaveBalanceOf0_naive() {

     Bank bank = new Bank();
     TransactionGenerator transactionGenerator = new TransactionGenerator(bank);

     int result = transactionGenerator.playTransactionSetOne();

     assertEquals(result, 30);
 }

Our test passes and our code is perfect, yay! What is this code actually testing? It’s testing all classes involved, which might actually be what we want or it might not be. This is really the problem here and with lots and lots of test code; the aim of the test must be clear and isolated before the test is written so that it’s clear what is actually under test. Although this example is very straightforward and it’s pretty obvious that our unit under test is vague, when systems become more complicated it’s not so straightforward.

Imagine if the Bank class actually performed a REST call to a remote endpoint to perform actions on the balance, then what is under test? Possibly the remote server infrastructure, possibly the remote server code or worse, the network link. Yes, these will need to be tested but it must not be forgotten that these elements exist in a system and must be isolated in certain levels of test.

How do we get around this? Mocking. Mocking is an awesome way to easily test code. There’s also loads of different frameworks available. The highest, least granular level is probably WireMock (http://wiremock.org/) which allows entire web services to be mocked so that server infrastructure and code can be isolated in a test environment. A bit lower down the stack, RESTAssured (https://code.google.com/p/rest-assured/) allows one to mock RESTful calls to a webserver so that no external calls actually occur. This is useful because it allows testing of code right up until it reaches the system boundary.

WireMock and RESTAssured are a little bit out of scope for this blog; we’re going to focus on two frameworks which work at a slightly lower level – specifically Mockito (http://mockito.org/) and touch on PowerMock (https://code.google.com/p/powermock/). These are useful in creating mock objects that replace the actual objects and allow one to interrogate how the functional code interacted with it; this really allows us to deeply check our code.

PowerMock provides more power than Mockito, often we find that we start with Mockito and then, when something is a bit more tricky to test, move on to PowerMock. There is a caveat with this – if you’re struggling to test your code with Mockito and find that you need to use PowerMock it might mean that the code is not as well written as it possibly could be. Consider refactoring before getting heavily involved in PowerMock. Generally, we find using the PowerMock whenNew() call to mock constructors is far as we ever need to go with PowerMock. Sometimes, especially if legacy code is involved, refactoring might not be an option. The golden rule? With great power comes great responsibility…

So, we could re-write our test method as follows to use Mockito:

@Test
public void shouldHaveBalanceOf30() throws Exception {

    Bank bank = mock(Bank.class);

    TransactionGenerator transactionGenerator = new TransactionGenerator(bank);

    int result = transactionGenerator.playTransactionSetOne();

    verify(bank, times(4)).incrementBalance();
    verify(bank, times(1)).decrementBalance();
    verify(bank, times(1)).getBalance();

    assertEquals(result, 0);
}

This code creates mocks for the object used by TransactionGenerator and then checks that the expected methods are called on it. Note that the test class should be annotated with @RunWith(MockitoJUnitRunner.class)  so that the members are mocked correctly.

We should also check that when unexpected behaviour occurs (ie – the balance could not be incremented or decremented and an exception is thrown) to confirm that the code behaves as expected in this situation. Note that the functional code has to change to:

public void incrementBalance() throws ConnectException {
    balance+=10;
}

public void decrementBalance() throws ConnectException {
    balance-=10;
}

public int getBalance() throws ConnectException {
    return balance;
}

We can assume that the exception would be thrown as part of a server call.

The test would look as follows:

   @Test(expected = Exception.class)
    public void shouldFailToCompleteTransaction() throws Exception {

        Bank bank = mock(Bank.class);

        doThrow(new ConnectException()).when(bank).decrementBalance();

        TransactionGenerator transactionGenerator = new TransactionGenerator(bank);

        int result = transactionGenerator.playTransactionSetOne();

        verify(bank, times(3)).incrementBalance();
        verify(bank, times(1)).decrementBalance();
    }
}

Note how we are now configuring the mock to throw the exception on a specific method call. It’s also possible to throw the exception based on the parameter passed (type or value) using Hamcrest matchers (https://code.google.com/p/hamcrest/wiki/Tutorial). Note that the exception is checked by adding to the JUnit @Test  annotation.

We hope you’re all testing gods now, go forth and test! Have a wonderful Christmas one and all!

~23Squared