Reputation: 307
I have a class Foo
with something like a locking mechanism. Let's call those methods lock()
, unlock()
and waitUntilUnlocked()
. The implementation of these methods is not of interest, since I want to test them (TDD).
Now I want to write a test testing that waitUntilUnlocked()
really waits until unlock()
is called. The idea I came up with is the following:
@Test
public void waitUntilUnlocked_waits() {
final Foo foo = createFoo();
final long[] durationInThread = new long[1];
foo.lock();
inThread(() -> {
durationInThread[0] = measureDurationOf(this::sleepSomeTime);
foo.unlock();
});
final long waitingTime = measureDurationOf(foo::waitUntilUnlocked);
assertThat(waitingTime).isGreaterThanOrEqualTo(durationInThread[0]);
}
Drawbacks: This works with sleep()
which makes it slow. Also, I get sometimes strange results that the assertion does not meet (The sleep time was 500ms, durationInThread[0]
was 501ms and waitingTime
was 498ms).
Do you have a better idea for a reliable and fast test, not open for race conditions?
Upvotes: 1
Views: 99
Reputation: 22910
The idea is to write a test which will eventually fail if your locks were bogus, but there is no one single fast and reliable black-box test when it comes to synchronization.
Imagine somebody else, a freelancer, was writing waitUntilUnlocked()
. He decides lazily to write it as
waitUntilUnlocked()
{
//haha I know they cannot lock for more than 10 sec, easy money
Thread.sleep(10*1000);
}
Your test will pass. You are trying to test that the caller of waitUntilUnlocked()
will wait forever, which cannot be tested.
A simpler way to test that it is the combination of these two tests.
The first one is your test a bit simplified. It tests the thread wait indeed at least some time when locked.
final Foo foo = createFoo();
foo.lock();
inThread(() -> {
foo.waitUntilUnlocked();
});
long time_millisec = 100;
inThread.join(time_millisec);
bool success = inThread.isAlive();
It never unlocks. time_millisec
is your parameter. No need to measure times.
The second one test that the thread make progress if unlocked. You could add a timer to measure the time before join completes.
final Foo foo = createFoo();
inThread(() -> {
foo.waitUntilUnlocked();
});
inThread.join();
return true; //success
You will execute anything after join only after waitUntilUnlocked
has terminated.
Black box testing shows its limits in your situation
Upvotes: 1