Latching and mocking in concurrent tests

Iwein Fuld

Concurrent testing is hard, but not as hard as you think. If you use the right tricks it can be done. This blog shows you one particular trick that uses a latch and a mock to ensure a test scenario is completed before running the verifications.

While working on Spring Integration in Action, I experimented with a neat solution for concurrent tests. When I showed it to some colleagues I was pleasantly surprised by the reaction that I got. Judge for yourself if it's worth the blog.

The main idea is to use a latch and let your mock count it down. It sounds trivial (and to be honest it is).

First let me show you how it would be done without mocking:

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ConcurrentTest {

  @Autowired @Qualifier("in") MessageChannel in;

  @Autowired @Qualifier("out") PollableChannel out;

  @Autowired Service service;

  @Test(timeout = 5000)
  public void shouldGoThroughPipeline() throws Exception {
    in.send(MessageBuilder.withPayload("test").build());

    service.invoked.await();
    out.receive();
  }

  public static class Service {
    private CountDownLatch invoked = new CountDownLatch(1);

    public String serve(String input) {
      invoked.countDown();
      return input;
    }
  }
}

A message is sent on channel "in", which will result in an asynchronous invocation of the service. I'm using Spring Integration to make the test asynchronous, but I could just as easily have used @Async, Camel, JMS or what not. The basic idea remains the same.

This is already mostly painless, but we clean up the test a bit more by using a mock. You can wire a mock into your context using a factory-method:

<bean id="service"  class="org.mockito.Mockito" factory-method="mock">
  <constructor-arg value="iwein.samples.test.concurrent.ConcurrentTest$Service"/>
</bean>

Now this mocked service will be autowired into the test and you can make it do tricks:

  @Test(timeout = 5000)
  public void shouldGoThroughPipeline() throws Exception {
    final CountDownLatch serviceInvoked = new CountDownLatch(1);
    given(service.serve("test")).willAnswer(new Answer(){
      public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
        serviceInvoked.countDown();
        return "test";
      }
    });

    in.send(MessageBuilder.withPayload("test").build());

    serviceInvoked.await();
    out.receive();
  }

The answer passed into willAnswer() method will first count down the latch and then return "test". This works, but it's not the most readable code. Let's tidy it up a bit:

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ConcurrentTest {

  @Autowired @Qualifier("in") MessageChannel in;

  @Autowired @Qualifier("out") PollableChannel out;

  @Autowired Service service;

  @Test(timeout = 5000)
  public void shouldGoThroughPipeline() throws Exception {
    //given
    final CountDownLatch serviceInvoked = new CountDownLatch(1);
    given(service.serve("test")).willAnswer(latchedAnswer("test", serviceInvoked));

    //when
    in.send(MessageBuilder.withPayload("test").build());
    serviceInvoked.await();

    //verify
    Message<?> message = out.receive();
    assertThat((String) message.getPayload(), is("test"));
  }

  private <T> Answer<T> latchedAnswer(final T returning, final CountDownLatch latch) {
    return new Answer(){
      public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
        latch.countDown();
        return returning;
      }
    };
  }

  //You'd have a class in main to mock, but an interface here will serve the example
  public static interface Service {
    public String serve(String input);
  }
}

That's really polite code for a concurrent test don't you think?

I'm thinking of adding the latchedAnswer method into a test utility that comes with Spring Integration, but as you can see it fits quite well in your copy buffer too. The point is that even without using a concurrent test framework (I know they're out there) simple concurrent tests can be as simple as they should be.

Caveats:

  • If you pass an instance of the wrong type into latchedAnswer you'll not get a compiler warning, because of a generics issue with Mockito
  • When you replace a service with a mock, frameworks using reflection around it might get a bit confused. For this example I had to add a method attribute to the service activator element that wouldn't have been needed without the mock.
  • Allways set a timeout. You don't want the test to run eternally on your build server if you are unfortunate enough to break it.

Source code for this example can be found on github. Enjoy!

Comments (4)

  1. David - Reply

    November 3, 2009 at 9:16 pm

    Latches are definitely useful in many situations. May I point you to a, in my opinion, related blog entry by Uncle Bob: http://blog.objectmentor.com/articles/2009/10/28/manual-mocking-resisting-the-invasion-of-dots-and-parentheses
    Personally I tend to lean toward using mocks, but I don't think there's any right way. As always, it's about using the right tool for the right job.

  2. Andrew Phillips - Reply

    November 3, 2009 at 11:31 pm

    Another interesting - if somewhat old - option for concurrent testing is Multithreaded TC; there have also been some recent efforts to integrate it with JUnit 4.
    That said, I haven't found a nice way to get it to do what your example does - provide a latch or delay within the flow of the code under test (in your example, in a mock). MTC is great in coordinating test threads but otherwise assumes that the code under test "naturally" blocks, e.g. when doing a take from a blocking queue.

  3. Andrew Dinn - Reply

    November 5, 2009 at 10:18 am

    You might also take a look at Byteman, a bytecode injection tool which was developed to address testing of multi-threaded apps. It makes this sort of thing trivial to achieve and avoids the need for mocking.

    Take a look at my paper in this year's Jazoon for an example of how to inject a synchronization point into a multi-threaded program, thereby exposing a concurrency bug. It requires a few lines of Byteman rule code and, more importantly, no edits to the original program. Countdowns are also provided as standard built-in operations. They can be used in Byteman conditions to decide whether or not to execute injected actions. Combined with a rendezvous action this can be used to ensure that threads synch up at the NTH call.

  4. Iwein Fuld - Reply

    November 12, 2009 at 1:11 pm

    I'm not a mock fanboy, but neither am I allergic to them like Uncle Bob.

    Byteman seems like a very interesting tool to test the concurrent nature of your program, whereas my trick merely allows you to deal with concurrency in a functional test. I'd say usually a trick like this is enough, but if you're chasing after concurrency bugs it pays off to look a little further.

Add a Comment