我有一个类Foo
,类似锁定机制。我们称这些方法为lock()
,unlock()
和waitUntilUnlocked()
。这些方法的实现并不重要,因为我想测试它们(TDD)。
现在我想编写一个测试测试waitUntilUnlocked()
实际上等待unlock()
被调用。我想出的想法如下:
@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]);
}
缺点:这适用于sleep()
,这使得它变慢。此外,我有时会得到奇怪的结果,断言不符合(睡眠时间为500毫秒,durationInThread[0]
为501毫秒,waitingTime
为498毫秒。“
您是否对可靠的和快速测试有更好的想法,而不是针对竞争条件开放?
答案 0 :(得分:1)
这个想法是编写一个测试,如果你的锁是假的,它最终会失败,但是在同步方面没有一个快速可靠的黑盒测试。
想象一下,其他人,一位自由撰稿人,正在撰写waitUntilUnlocked()
。他懒洋洋地决定把它写成
waitUntilUnlocked()
{
//haha I know they cannot lock for more than 10 sec, easy money
Thread.sleep(10*1000);
}
你的考试将通过。您正试图测试waitUntilUnlocked()
的来电者是否会等待 永远 ,这是无法测试的。
一种更简单的方法来测试它是这两个测试的组合。
第一个是你的测试有点简化。它在锁定时测试线程确实等待 至少一段时间 。
final Foo foo = createFoo();
foo.lock();
inThread(() -> {
foo.waitUntilUnlocked();
});
long time_millisec = 100;
inThread.join(time_millisec);
bool success = inThread.isAlive();
永远不会解锁。 time_millisec
是您的参数。无需衡量时间。
第二个测试线程在解锁时取得进展。您可以添加一个计时器来测量连接完成之前的时间。
final Foo foo = createFoo();
inThread(() -> {
foo.waitUntilUnlocked();
});
inThread.join();
return true; //success
只有在waitUntilUnlocked
终止后才会在加入后执行任何操作。
黑盒测试显示其在您情况下的限制