1*cc02d7e2SAndroid Build Coastguard Worker# How to write unit tests for gRPC C client. 2*cc02d7e2SAndroid Build Coastguard Worker 3*cc02d7e2SAndroid Build Coastguard Workertl;dr: [Example code](https://github.com/grpc/grpc/blob/master/test/cpp/end2end/mock_test.cc). 4*cc02d7e2SAndroid Build Coastguard Worker 5*cc02d7e2SAndroid Build Coastguard WorkerTo 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. 6*cc02d7e2SAndroid Build Coastguard Worker 7*cc02d7e2SAndroid Build Coastguard WorkerFor instance, consider an EchoService like this: 8*cc02d7e2SAndroid Build Coastguard Worker 9*cc02d7e2SAndroid Build Coastguard Worker 10*cc02d7e2SAndroid Build Coastguard Worker```proto 11*cc02d7e2SAndroid Build Coastguard Workerservice EchoTestService { 12*cc02d7e2SAndroid Build Coastguard Worker rpc Echo(EchoRequest) returns (EchoResponse); 13*cc02d7e2SAndroid Build Coastguard Worker rpc BidiStream(stream EchoRequest) returns (stream EchoResponse); 14*cc02d7e2SAndroid Build Coastguard Worker} 15*cc02d7e2SAndroid Build Coastguard Worker``` 16*cc02d7e2SAndroid Build Coastguard Worker 17*cc02d7e2SAndroid Build Coastguard WorkerThe code generated would look something like this: 18*cc02d7e2SAndroid Build Coastguard Worker 19*cc02d7e2SAndroid Build Coastguard Worker```c 20*cc02d7e2SAndroid Build Coastguard Workerclass EchoTestService final { 21*cc02d7e2SAndroid Build Coastguard Worker public: 22*cc02d7e2SAndroid Build Coastguard Worker class StubInterface { 23*cc02d7e2SAndroid Build Coastguard Worker virtual ::grpc::Status Echo(::grpc::ClientContext* context, const ::grpc::testing::EchoRequest& request, ::grpc::testing::EchoResponse* response) = 0; 24*cc02d7e2SAndroid Build Coastguard Worker … 25*cc02d7e2SAndroid Build Coastguard Worker std::unique_ptr< ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>> BidiStream(::grpc::ClientContext* context) { 26*cc02d7e2SAndroid Build Coastguard Worker return std::unique_ptr< ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>>(BidiStreamRaw(context)); 27*cc02d7e2SAndroid Build Coastguard Worker } 28*cc02d7e2SAndroid Build Coastguard Worker … 29*cc02d7e2SAndroid Build Coastguard Worker private: 30*cc02d7e2SAndroid Build Coastguard Worker virtual ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>* BidiStreamRaw(::grpc::ClientContext* context) = 0; 31*cc02d7e2SAndroid Build Coastguard Worker … 32*cc02d7e2SAndroid Build Coastguard Worker } // End StubInterface 33*cc02d7e2SAndroid Build Coastguard Worker… 34*cc02d7e2SAndroid Build Coastguard Worker} // End EchoTestService 35*cc02d7e2SAndroid Build Coastguard Worker``` 36*cc02d7e2SAndroid Build Coastguard Worker 37*cc02d7e2SAndroid Build Coastguard Worker 38*cc02d7e2SAndroid Build Coastguard WorkerIf we mock the StubInterface and set expectations on the pure-virtual methods we can test client-side logic without having to make any rpcs. 39*cc02d7e2SAndroid Build Coastguard Worker 40*cc02d7e2SAndroid Build Coastguard WorkerA mock for this StubInterface will look like this: 41*cc02d7e2SAndroid Build Coastguard Worker 42*cc02d7e2SAndroid Build Coastguard Worker 43*cc02d7e2SAndroid Build Coastguard Worker```c 44*cc02d7e2SAndroid Build Coastguard Workerclass MockEchoTestServiceStub : public EchoTestService::StubInterface { 45*cc02d7e2SAndroid Build Coastguard Worker public: 46*cc02d7e2SAndroid Build Coastguard Worker MOCK_METHOD3(Echo, ::grpc::Status(::grpc::ClientContext* context, const ::grpc::testing::EchoRequest& request, ::grpc::testing::EchoResponse* response)); 47*cc02d7e2SAndroid Build Coastguard Worker MOCK_METHOD1(BidiStreamRaw, ::grpc::ClientReaderWriterInterface< ::grpc::testing::EchoRequest, ::grpc::testing::EchoResponse>*(::grpc::ClientContext* context)); 48*cc02d7e2SAndroid Build Coastguard Worker}; 49*cc02d7e2SAndroid Build Coastguard Worker``` 50*cc02d7e2SAndroid Build Coastguard Worker 51*cc02d7e2SAndroid Build Coastguard Worker 52*cc02d7e2SAndroid Build Coastguard Worker**Generating mock code:** 53*cc02d7e2SAndroid Build Coastguard Worker 54*cc02d7e2SAndroid Build Coastguard WorkerSuch a mock can be auto-generated by: 55*cc02d7e2SAndroid Build Coastguard Worker 56*cc02d7e2SAndroid Build Coastguard Worker 57*cc02d7e2SAndroid Build Coastguard Worker 58*cc02d7e2SAndroid Build Coastguard Worker1. Setting flag(generate_mock_code=true) on grpc plugin for protoc, or 59*cc02d7e2SAndroid Build Coastguard Worker1. Setting an attribute(generate_mocks) in your bazel rule. 60*cc02d7e2SAndroid Build Coastguard Worker 61*cc02d7e2SAndroid Build Coastguard WorkerProtoc plugin flag: 62*cc02d7e2SAndroid Build Coastguard Worker 63*cc02d7e2SAndroid Build Coastguard Worker```sh 64*cc02d7e2SAndroid Build Coastguard Workerprotoc -I . --grpc_out=generate_mock_code=true:. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` echo.proto 65*cc02d7e2SAndroid Build Coastguard Worker``` 66*cc02d7e2SAndroid Build Coastguard Worker 67*cc02d7e2SAndroid Build Coastguard WorkerBazel rule: 68*cc02d7e2SAndroid Build Coastguard Worker 69*cc02d7e2SAndroid Build Coastguard Worker```py 70*cc02d7e2SAndroid Build Coastguard Workergrpc_proto_library( 71*cc02d7e2SAndroid Build Coastguard Worker name = "echo_proto", 72*cc02d7e2SAndroid Build Coastguard Worker srcs = ["echo.proto"], 73*cc02d7e2SAndroid Build Coastguard Worker generate_mocks = True, 74*cc02d7e2SAndroid Build Coastguard Worker) 75*cc02d7e2SAndroid Build Coastguard Worker``` 76*cc02d7e2SAndroid Build Coastguard Worker 77*cc02d7e2SAndroid Build Coastguard Worker 78*cc02d7e2SAndroid Build Coastguard WorkerBy adding such a flag now a header file `echo_mock.grpc.pb.h` containing the mocked stub will also be generated. 79*cc02d7e2SAndroid Build Coastguard Worker 80*cc02d7e2SAndroid Build Coastguard WorkerThis header file can then be included in test files along with a gmock dependency. 81*cc02d7e2SAndroid Build Coastguard Worker 82*cc02d7e2SAndroid Build Coastguard Worker**Writing tests with mocked Stub.** 83*cc02d7e2SAndroid Build Coastguard Worker 84*cc02d7e2SAndroid Build Coastguard WorkerConsider the following client a user might have: 85*cc02d7e2SAndroid Build Coastguard Worker 86*cc02d7e2SAndroid Build Coastguard Worker```c 87*cc02d7e2SAndroid Build Coastguard Workerclass FakeClient { 88*cc02d7e2SAndroid Build Coastguard Worker public: 89*cc02d7e2SAndroid Build Coastguard Worker explicit FakeClient(EchoTestService::StubInterface* stub) : stub_(stub) {} 90*cc02d7e2SAndroid Build Coastguard Worker 91*cc02d7e2SAndroid Build Coastguard Worker void DoEcho() { 92*cc02d7e2SAndroid Build Coastguard Worker ClientContext context; 93*cc02d7e2SAndroid Build Coastguard Worker EchoRequest request; 94*cc02d7e2SAndroid Build Coastguard Worker EchoResponse response; 95*cc02d7e2SAndroid Build Coastguard Worker request.set_message("hello world"); 96*cc02d7e2SAndroid Build Coastguard Worker Status s = stub_->Echo(&context, request, &response); 97*cc02d7e2SAndroid Build Coastguard Worker EXPECT_EQ(request.message(), response.message()); 98*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(s.ok()); 99*cc02d7e2SAndroid Build Coastguard Worker } 100*cc02d7e2SAndroid Build Coastguard Worker 101*cc02d7e2SAndroid Build Coastguard Worker void DoBidiStream() { 102*cc02d7e2SAndroid Build Coastguard Worker EchoRequest request; 103*cc02d7e2SAndroid Build Coastguard Worker EchoResponse response; 104*cc02d7e2SAndroid Build Coastguard Worker ClientContext context; 105*cc02d7e2SAndroid Build Coastguard Worker std::string msg("hello"); 106*cc02d7e2SAndroid Build Coastguard Worker 107*cc02d7e2SAndroid Build Coastguard Worker std::unique_ptr<ClientReaderWriterInterface<EchoRequest, EchoResponse>> 108*cc02d7e2SAndroid Build Coastguard Worker stream = stub_->BidiStream(&context); 109*cc02d7e2SAndroid Build Coastguard Worker 110*cc02d7e2SAndroid Build Coastguard Worker request.set_message(msg "0"); 111*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Write(request)); 112*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Read(&response)); 113*cc02d7e2SAndroid Build Coastguard Worker EXPECT_EQ(response.message(), request.message()); 114*cc02d7e2SAndroid Build Coastguard Worker 115*cc02d7e2SAndroid Build Coastguard Worker request.set_message(msg "1"); 116*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Write(request)); 117*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Read(&response)); 118*cc02d7e2SAndroid Build Coastguard Worker EXPECT_EQ(response.message(), request.message()); 119*cc02d7e2SAndroid Build Coastguard Worker 120*cc02d7e2SAndroid Build Coastguard Worker request.set_message(msg "2"); 121*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Write(request)); 122*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(stream->Read(&response)); 123*cc02d7e2SAndroid Build Coastguard Worker EXPECT_EQ(response.message(), request.message()); 124*cc02d7e2SAndroid Build Coastguard Worker 125*cc02d7e2SAndroid Build Coastguard Worker stream->WritesDone(); 126*cc02d7e2SAndroid Build Coastguard Worker EXPECT_FALSE(stream->Read(&response)); 127*cc02d7e2SAndroid Build Coastguard Worker 128*cc02d7e2SAndroid Build Coastguard Worker Status s = stream->Finish(); 129*cc02d7e2SAndroid Build Coastguard Worker EXPECT_TRUE(s.ok()); 130*cc02d7e2SAndroid Build Coastguard Worker } 131*cc02d7e2SAndroid Build Coastguard Worker 132*cc02d7e2SAndroid Build Coastguard Worker void ResetStub(EchoTestService::StubInterface* stub) { stub_ = stub; } 133*cc02d7e2SAndroid Build Coastguard Worker 134*cc02d7e2SAndroid Build Coastguard Worker private: 135*cc02d7e2SAndroid Build Coastguard Worker EchoTestService::StubInterface* stub_; 136*cc02d7e2SAndroid Build Coastguard Worker}; 137*cc02d7e2SAndroid Build Coastguard Worker``` 138*cc02d7e2SAndroid Build Coastguard Worker 139*cc02d7e2SAndroid Build Coastguard WorkerA test could initialize this FakeClient with a mocked stub having set expectations on it: 140*cc02d7e2SAndroid Build Coastguard Worker 141*cc02d7e2SAndroid Build Coastguard WorkerUnary RPC: 142*cc02d7e2SAndroid Build Coastguard Worker 143*cc02d7e2SAndroid Build Coastguard Worker```c 144*cc02d7e2SAndroid Build Coastguard WorkerMockEchoTestServiceStub stub; 145*cc02d7e2SAndroid Build Coastguard WorkerEchoResponse resp; 146*cc02d7e2SAndroid Build Coastguard Workerresp.set_message("hello world"); 147*cc02d7e2SAndroid Build Coastguard WorkerEXPECT_CALL(stub, Echo(_,_,_)).Times(AtLeast(1)).WillOnce(DoAll(SetArgPointee<2>(resp), Return(Status::OK))); 148*cc02d7e2SAndroid Build Coastguard WorkerFakeClient client(stub); 149*cc02d7e2SAndroid Build Coastguard Workerclient.DoEcho(); 150*cc02d7e2SAndroid Build Coastguard Worker``` 151*cc02d7e2SAndroid Build Coastguard Worker 152*cc02d7e2SAndroid Build Coastguard WorkerStreaming RPC: 153*cc02d7e2SAndroid Build Coastguard Worker 154*cc02d7e2SAndroid Build Coastguard Worker```c 155*cc02d7e2SAndroid Build Coastguard WorkerACTION_P(copy, msg) { 156*cc02d7e2SAndroid Build Coastguard Worker arg0->set_message(msg->message()); 157*cc02d7e2SAndroid Build Coastguard Worker} 158*cc02d7e2SAndroid Build Coastguard Worker 159*cc02d7e2SAndroid Build Coastguard Worker 160*cc02d7e2SAndroid Build Coastguard Workerauto rw = new MockClientReaderWriter<EchoRequest, EchoResponse>(); 161*cc02d7e2SAndroid Build Coastguard WorkerEchoRequest msg; 162*cc02d7e2SAndroid Build Coastguard WorkerEXPECT_CALL(*rw, Write(_, _)).Times(3).WillRepeatedly(DoAll(SaveArg<0>(&msg), Return(true))); 163*cc02d7e2SAndroid Build Coastguard WorkerEXPECT_CALL(*rw, Read(_)). 164*cc02d7e2SAndroid Build Coastguard Worker WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))). 165*cc02d7e2SAndroid Build Coastguard Worker WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))). 166*cc02d7e2SAndroid Build Coastguard Worker WillOnce(DoAll(WithArg<0>(copy(&msg)), Return(true))). 167*cc02d7e2SAndroid Build Coastguard Worker WillOnce(Return(false)); 168*cc02d7e2SAndroid Build Coastguard Worker 169*cc02d7e2SAndroid Build Coastguard WorkerMockEchoTestServiceStub stub; 170*cc02d7e2SAndroid Build Coastguard WorkerEXPECT_CALL(stub, BidiStreamRaw(_)).Times(AtLeast(1)).WillOnce(Return(rw)); 171*cc02d7e2SAndroid Build Coastguard Worker 172*cc02d7e2SAndroid Build Coastguard WorkerFakeClient client(stub); 173*cc02d7e2SAndroid Build Coastguard Workerclient.DoBidiStream(); 174*cc02d7e2SAndroid Build Coastguard Worker``` 175*cc02d7e2SAndroid Build Coastguard Worker 176