|  | @@ -0,0 +1,175 @@
 | 
	
		
			
				|  |  | +# How to write unit tests for gRPC C++ client.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +tl;dr: [Example code](https://github.com/grpc/grpc/blob/master/test/cpp/end2end/mock_test.cc).
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +To unit-test client-side logic via the synchronous API, gRPC provides a mocked Stub based on googletest(googlemock) that can be programmed upon and easily incorporated in the test code. 
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +For instance, consider an EchoService like this:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```proto
 | 
	
		
			
				|  |  | +service EchoTestService {
 | 
	
		
			
				|  |  | +        rpc Echo(EchoRequest) returns (EchoResponse);
 | 
	
		
			
				|  |  | +        rpc BidiStream(stream EchoRequest) returns (stream EchoResponse);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +The code generated would look something like this:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```c++
 | 
	
		
			
				|  |  | +class EchoTestService final {
 | 
	
		
			
				|  |  | +  public:
 | 
	
		
			
				|  |  | +  class StubInterface {
 | 
	
		
			
				|  |  | +    virtual ::grpc::Status Echo(::grpc::ClientContext* context, const ::grpc::testing::EchoRequest& request, ::grpc::testing::EchoResponse* response) = 0;
 | 
	
		
			
				|  |  | +  …
 | 
	
		
			
				|  |  | +    std::unique_ptr< ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>> BidiStream(::grpc::ClientContext* context) {
 | 
	
		
			
				|  |  | +      return std::unique_ptr< ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>>(BidiStreamRaw(context));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  …
 | 
	
		
			
				|  |  | +    private:
 | 
	
		
			
				|  |  | +    virtual ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>* BidiStreamRaw(::grpc::ClientContext* context) = 0;
 | 
	
		
			
				|  |  | +  …
 | 
	
		
			
				|  |  | +  } // End StubInterface
 | 
	
		
			
				|  |  | +…
 | 
	
		
			
				|  |  | +} // End EchoTestService
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +If we mock the StubInterface and set expectations on the pure-virtual methods we can test client-side logic without having to make any rpcs.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +A mock for this StubInterface will look like this:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```c++
 | 
	
		
			
				|  |  | +class MockEchoTestServiceStub : public EchoTestService::StubInterface {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  MOCK_METHOD3(Echo, ::grpc::Status(::grpc::ClientContext* context, const ::grpc::testing::EchoRequest& request, ::grpc::testing::EchoResponse* response));
 | 
	
		
			
				|  |  | +  MOCK_METHOD1(BidiStreamRaw, ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>*(::grpc::ClientContext* context));
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +**Generating mock code:**
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Such a mock can be auto-generated by:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +1.  Setting flag(generate_mock_code=true) on grpc plugin for protoc, or
 | 
	
		
			
				|  |  | +1.  Setting an attribute(generate_mock) in your bazel rule.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Protoc plugin flag:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```sh
 | 
	
		
			
				|  |  | +protoc -I . --grpc_out=generate_mock_code=true:. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` echo.proto
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Bazel rule:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```py
 | 
	
		
			
				|  |  | +grpc_proto_library(
 | 
	
		
			
				|  |  | +  name = "echo_proto",
 | 
	
		
			
				|  |  | +  srcs = ["echo.proto"],
 | 
	
		
			
				|  |  | +  generate_mock = True, 
 | 
	
		
			
				|  |  | +)
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +By adding such a flag now a header file `echo_mock.grpc.pb.h` containing the mocked stub will also be generated. 
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +This header file can then be included in test files along with a gmock dependency.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +**Writing tests with mocked Stub.**
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Consider the following client a user might have:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```c++
 | 
	
		
			
				|  |  | +class FakeClient {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  explicit FakeClient(EchoTestService::StubInterface* stub) : stub_(stub) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void DoEcho() {
 | 
	
		
			
				|  |  | +    ClientContext context;
 | 
	
		
			
				|  |  | +    EchoRequest request;
 | 
	
		
			
				|  |  | +    EchoResponse response;
 | 
	
		
			
				|  |  | +    request.set_message("hello world");
 | 
	
		
			
				|  |  | +    Status s = stub_->Echo(&context, request, &response);
 | 
	
		
			
				|  |  | +    EXPECT_EQ(request.message(), response.message());
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(s.ok());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void DoBidiStream() {
 | 
	
		
			
				|  |  | +    EchoRequest request;
 | 
	
		
			
				|  |  | +    EchoResponse response;
 | 
	
		
			
				|  |  | +    ClientContext context;
 | 
	
		
			
				|  |  | +    grpc::string msg("hello");
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    std::unique_ptr<ClientReaderWriterInterface<EchoRequest, EchoResponse>>
 | 
	
		
			
				|  |  | +        stream = stub_->BidiStream(&context);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    request.set_message(msg + "0");
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Write(request));
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Read(&response));
 | 
	
		
			
				|  |  | +    EXPECT_EQ(response.message(), request.message());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    request.set_message(msg + "1");
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Write(request));
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Read(&response));
 | 
	
		
			
				|  |  | +    EXPECT_EQ(response.message(), request.message());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    request.set_message(msg + "2");
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Write(request));
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(stream->Read(&response));
 | 
	
		
			
				|  |  | +    EXPECT_EQ(response.message(), request.message());
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    stream->WritesDone();
 | 
	
		
			
				|  |  | +    EXPECT_FALSE(stream->Read(&response));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Status s = stream->Finish();
 | 
	
		
			
				|  |  | +    EXPECT_TRUE(s.ok());
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ResetStub(EchoTestService::StubInterface* stub) { stub_ = stub; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  EchoTestService::StubInterface* stub_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +A test could initialize this FakeClient with a mocked stub having set expectations on it:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Unary RPC:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```c++
 | 
	
		
			
				|  |  | +MockEchoTestServiceStub stub;
 | 
	
		
			
				|  |  | +EchoResponse resp;
 | 
	
		
			
				|  |  | +resp.set_message("hello world");
 | 
	
		
			
				|  |  | +Expect_CALL(stub, Echo(_,_,_)).Times(Atleast(1)).WillOnce(DoAll(SetArgPointee<2>(resp), Return(Status::OK)));
 | 
	
		
			
				|  |  | +FakeClient client(stub);
 | 
	
		
			
				|  |  | +client.DoEcho();
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +Streaming RPC:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +```c++
 | 
	
		
			
				|  |  | +ACTION_P(copy, msg) {
 | 
	
		
			
				|  |  | +  arg0->set_message(msg->message());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +auto rw = new MockClientReaderWriter<EchoRequest, EchoResponse>();
 | 
	
		
			
				|  |  | +EchoRequest msg;
 | 
	
		
			
				|  |  | +EXPECT_CALL(*rw, Write(_, _)).Times(3).WillRepeatedly(DoAll(SaveArg<0>(&msg), Return(true)));
 | 
	
		
			
				|  |  | +EXPECT_CALL(*rw, Read(_)).
 | 
	
		
			
				|  |  | +      WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
 | 
	
		
			
				|  |  | +      WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
 | 
	
		
			
				|  |  | +      WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))).
 | 
	
		
			
				|  |  | +      WillOnce(Return(false));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +MockEchoTestServiceStub  stub;
 | 
	
		
			
				|  |  | +EXPECT_CALL(stub, BidiStreamRaw(_)).Times(AtLeast(1)).WillOnce(Return(rw));
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +FakeClient client(stub);
 | 
	
		
			
				|  |  | +client.DoBidiStream();
 | 
	
		
			
				|  |  | +```
 | 
	
		
			
				|  |  | +
 |