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:
Source code for this example can be found on github. Enjoy!
Tags: Opensource, Spring, TDD
Filed under Java | 4 Comments »
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.
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.
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.
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.