Java Reference
In-Depth Information
The test includes a timeout in case the client thread aborts and can't complete the count-
down; but if everything goes as expected, the updated test finishes in under a second:
$ ant test
...
[junit] Running org.foo.mock.LogClientTests
[junit] <--> thread="LogService Tester", bundle=0 : LogService has gone
[junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.14 sec
So far so good: all you have to do to test additional log calls is increment the latch
count. But what should you do if your client thread doesn't contain a pause method
or this method can't be replaced or extended? Another solution is to add barriers to
the mocked-out objects themselves by using so-called answer objects . Answers let you
perform basic AOP by intercepting method calls, which you can use to add synchroni-
zation points. Here's an example:
expect(context.getServiceReference(isA(String.class)).andAnswer(
new IAnswer<ServiceReference>() {
public ServiceReference answer() {
LockSupport.park();
return null;
}
});
In this (incomplete) example, you script an answer that always returns a null service ref-
erence and use it to suspend the client thread whenever it makes this call. This works
as long as the client thread initiates the expected call at the right time and there are no
problems with suspending the client in the middle of this call. But it also leaves the client
code untouched, which in this case means a five-second pause between log calls. You'll
test another log call in the next section, so let's stick with the original latch solution.
7.2.5
Exposing race conditions
OSG i is dynamic: bundles and services may come and go at any time. The key to devel-
oping a robust application is being able to cope with and react to these events. This
same dynamism makes testing robustness difficult. You could deploy your bundles
into a real framework and attempt to script events to cover all possibilities (we'll look
at this in more detail in section 7.3), but some scenarios require microsecond timing.
Remember the race condition we mentioned at the start of this section? This will be
exposed only if you can arrange for the LogService to disappear between two method
invocations—a narrow window. Many factors can cause you to miss this window, such
as unexpected garbage collection and differences in thread scheduling. With mock-
ing, you can easily script the exact sequence of events you want:
expect(context.getServiceReference(LogService.class.getName()))
.andReturn(serviceRef);
expect(context.getService(serviceRef))
.andReturn(null);
expect(context.getBundle())
.andReturn(bundle).anyTimes();
Search WWH ::




Custom Search