我想模拟我的grpc客户端,以确保它可以通过抛出new StatusRuntimeException(Status.UNAVAILABLE)
来抵抗故障(这是向java.net.ConnectException: Connection refused
抛出grpc客户端时抛出的异常)。但是,生成的类是最终的,因此模拟无法正常工作。
如何使BlahServiceBlockingStub抛出new StatusRuntimeException(Status.UNAVAILABLE)
而不必重构代码以围绕BlahServiceBlockingStub创建包装器类?
这是我尝试过的(由grpc生成BlahServiceBlockingStub):
@Test
public void test() {
BlahServiceBlockingStub blahServiceBlockingStub = mock(BlahServiceBlockingStub.class);
when(blahServiceBlockingStub.blah(any())).thenThrow(new StatusRuntimeException(Status.UNAVAILABLE));
blahServiceBlockingStub.blah(null);
}
不幸的是,我得到了以下异常:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class BlahServiceGrpc$BlahServiceBlockingStub
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
at MyTestClass.test(MyTestClass.java:655)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
.
.
.
因为我尝试模拟grpc生成的最终类:
public static final class BlahServiceBlockingStub extends io.grpc.stub.AbstractStub<BlahServiceBlockingStub> {
private BlahServiceBlockingStub(io.grpc.Channel channel) {
super(channel);
}
答案 0 :(得分:3)
您有几种选择:
请注意,为什么在这种情况下嘲笑final可能不是一个好主意: 根据具体情况,模拟最终类或方法可能不是一个好主意。细节决定成败。根据您的情况,您正在创建生成代码的模拟,因此您假设该生成代码在将来的行为。 gRPC和Protobuf仍在快速发展,因此做出这些假设可能会冒险,因为它们可能会更改,您不会注意到,因为您没有针对生成的代码检查模拟。因此,除非确实需要,否则模拟生成的代码不是一个好主意。
答案 1 :(得分:2)
请勿嘲笑客户端存根或任何其他最终类/方法。 gRPC团队可能会竭尽全力破坏您对此类模拟的使用,因为它们非常脆弱,并且会产生“不可能”的结果。
模拟服务,而不是客户端存根。与过程中运输结合使用时,它可以进行快速,可靠的测试。这与grpc-java hello world example中演示的方法相同。
-Dprofile=prod
进行多个测试时,您可以将服务器,通道和存根的创建提升到各个测试中的字段或@Rule
public final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
@Test
public void test() {
// This can be a mock, but is easier here as a fake implementation
BlahServiceImplBase serviceImpl = new BlahServiceImplBase() {
@Override public void blah(Request req, StreamObserver<Response> resp) {
resp.onError(new StatusRuntimeException(Status.UNAVAILABLE));
}
};
// Note that the channel and server can be created in any order
grpcCleanup.register(InProcessServerBuilder.forName("mytest")
.directExecutor().addService(serviceImpl).build().start());
ManagedChannel chan = grpcCleanup.register(
InProcessChannelBuilder.forName("mytest").directExecutor().build();
BlahServiceBlockingStub blahServiceBlockingStub
= BlahServiceGrpc.newBlockingStub();
blahServiceBlockingStub.blah(null);
}
中。这样做时,可以方便地在服务器上将@Before
用作MutableHandlerRegistry
。这样,您就可以在服务器启动后注册服务。有关该方法的完整示例,请参见route guide example。
答案 2 :(得分:0)
如何使用Mockito模拟最终课程/方法:
添加依赖项Mockito Inline
创建文件src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
仅一行:mock-maker-inline
现在您可以模拟最终方法和类。
答案 3 :(得分:0)
我最终遇到了一个丑陋的解决方法。
我在类上创建了一个新方法,并且在类中引用了BlahServiceBlockingStub。
生成的代码最终看起来像:
spy()