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