上下文: 我正在为gRPC服务编写单元测试。我想验证是否在服务器端调用了模拟方法。我正在使用简单的模拟。为了确保我们能得到gRPC的响应(无论它是什么),我都需要先挂起线程,然后再进行简单的模拟验证调用。
所以我尝试使用LockSupport这样的事情:
@Test
public void alphaMethodTest() throws Exception
{
Dummy dummy = createNiceMock(Dummy.class);
dummy.alphaMethod(anyBoolean());
expectLastCall().once();
EasyMock.replay(dummy);
DummyServiceGrpcImpl dummyServiceGrpc = new DummyServiceGrpcImpl();
bcreuServiceGrpc.setDummy(dummy);
DummyServiceGrpc.DummyServiceStub stub = setupDummyServiceStub();
Thread thread = Thread.currentThread();
stub.alphaMethod(emptyRequest, new StreamObserver<X>(){
@Override
public void onNext(X value) {
LockSupport.unpark(thread);
}
}
Instant expirationTime = Instant.now().plus(pDuration);
LockSupport.parkUntil(expirationTime.toEpochMilli());
verify(dummy);
}
但是我有很多这样的测试(大约40个),我怀疑线程问题。我通常会在一到两个未通过验证步骤的情况下,有时全部都通过。我尝试使用带有Condition的ReentrantLock。但是又有一些失败了(signalAll上的IllegalMonitorStateException):
@Test
public void alphaMethodTest() throws Exception
{
Dummy dummy = createNiceMock(Dummy.class);
dummy.alphaMethod(anyBoolean());
expectLastCall().once();
EasyMock.replay(dummy);
DummyServiceGrpcImpl dummyServiceGrpc = new DummyServiceGrpcImpl();
bcreuServiceGrpc.setDummy(dummy);
DummyServiceGrpc.DummyServiceStub stub = setupDummyServiceStub();
ReentrantLock lock = new ReentrantLock();
Condition conditionPromiseTerminated = lock.newCondition();
stub.alphaMethod(emptyRequest, new StreamObserver<X>(){
@Override
public void onNext(X value) {
conditionPromiseTerminated.signalAll();
}
}
Instant expirationTime = Instant.now().plus(pDuration);
conditionPromiseTerminated.awaitUntil(new Date(expirationTime.toEpochMilli()));
verify(dummy);
}
很抱歉,没有为您提供可运行的示例,我当前的代码正在使用私有API:/。
您认为LockSupport可能会因为运行多个测试而引起麻烦?我是否缺少使用锁支持或可重入锁的东西。您是否认为其他任何并发API类都可以更好地满足我的需求?
答案 0 :(得分:0)
LockSupport
有点危险,您需要仔细阅读文档并了解:
虚假地(即无故)呼叫返回。
因此,当您认为您的代码将进行“等待”时,它可能会立即返回。最简单的原因是this for example,但也可能有其他原因。
使用ReentrantLock
时,所有这些都应该以{{1}}失败,因为您永远不会通过IllegalMonitorStateException
获取锁。并停止使用ReentrantLock::lock
,它已被弃用是有原因的。
我认为您过于复杂,可以使用 plain 锁(简化的示例)进行相同的信号发送:
new Date(...)
注意输出:
public static void main(String[] args) {
Object lock = new Object();
Thread first = new Thread(() -> {
synchronized (lock) {
System.out.println("Locked");
try {
System.out.println("Sleeping");
lock.wait();
System.out.println("Waked up");
} catch (InterruptedException e) {
// these are your tests, no one should interrupt
// unless it's yourself
throw new RuntimeException(e);
}
}
});
first.start();
sleepOneSecond();
Thread second = new Thread(() -> {
synchronized (lock) {
System.out.println("notifying waiting threads");
lock.notify();
}
});
second.start();
}
private static void sleepOneSecond() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
很明显,线程之间的“通信”(信号)是如何发生的。