xref: /aosp_15_r20/external/grpc-grpc/doc/unit_testing.md (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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 Worker25*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 Worker29*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 Worker32*cc02d7e2SAndroid Build Coastguard Worker  } // End StubInterface
33*cc02d7e2SAndroid Build Coastguard Worker34*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