grpc / grpc-java

The Java gRPC implementation. HTTP/2 based RPC
https://grpc.io/docs/languages/java/
Apache License 2.0
11.38k stars 3.83k forks source link

Stub classes shouldn't be final to allow mocking #2160

Closed anuraaga closed 8 years ago

anuraaga commented 8 years ago

2077 Seems to have made stub classes final. As stubs do remote calls, it's usually important for them to be mockable to write unit tests, but mockito can't mock final classes. Previously users could use the interface I guess. While I understand the reasoning of dropping the interface for service classes in #1469, to allow stub upgrades without breaking service implementation code compile, does this apply to client stubs too? It'd be nice for the stub classes to either be non-final or have interfaces to allow mocking.

ejona86 commented 8 years ago

It was never our intention to allow mocking the stubs. Any time Mockito does its hacks to mock classes that shouldn't be mocked (like if they only have private constructors, like AbstractStub) it opens the door to us breaking your test. We can't support tests that use Mockito to avoid API restrictions.

https://github.com/grpc/grpc-java/issues/1469#issuecomment-226873511 has some specific issues with mocking the stub. Instead of mocking the stub, we would suggest you to mock a service implementation and use InProcess transport for connecting the two. If you use directExecutor() for both client and server, all the calls happen immediately and within the testing thread.

If there are use cases that are poorly supported or utilities that could be useful (like #2051), we'd be happy to hear about them.

srivastavag commented 7 years ago

This thread is somewhat dated so not sure if info is upto but cant StubInterface be used for mocking ? Thanks

ejona86 commented 7 years ago

@srivastavag, there isn't a StubInterface in the Java generated code.

srivastavag commented 7 years ago

Oops, I was looking for c++, extremely sorry for my oversight.

ensonic commented 6 years ago

@ejona86 All the linked examples assume you mock the service you are testing, which sounds weird to me (see https://github.com/grpc/grpc-java/blob/master/examples/src/test/java/io/grpc/examples/helloworld/HelloWorldClientTest.java) My grpc service talks to other grpc services and I'd like to mock out those. My Dagger module injects those secondary grpc service stubs and hence in the Test dagger module I'll need to provide mocked stubs and this is when things seem to fall apart.

I understand your suggestion to mock the impl instead, but I am not sure how to actually make this work. I have this code:

@Module
public class XyzTestModule {

  @Provides
  BindableService provideXzyGrpcService(Datastore datastore) {
    GrpcServerRule grpcServerRule = new GrpcServerRule().directExecutor();
    AbcGrpc.AbcImplBase abcImpl =
      mock(AbcGrpc.AbcImplBase.class, delegatesTo(new AbcGrpc.AbcImplBase() {}));

    grpcServerRule.getServiceRegistry().addService(abcImpl);
    AbcGrpc.AbcBlockingStub abcClient =  AbcGrpc.newBlockingStub(grpcServerRule.getChannel());

    return new XyzService(datastore, abcClient);
  }
}

but get NullPointerExceptions for grpcServerRule.getServiceRegistry().addService(abcImpl); and this is probably because I run this outside of the @Rule and the GrpcServerRule might need to be started or something (probably whatever the protected before() method does). Getting some real-world example would be awesome, since this is not obvious.

It looks like I'll have to replicate most of what is in https://github.com/grpc/grpc-java/blob/master/testing/src/main/java/io/grpc/testing/GrpcServerRule.java, right?