From 012e929a1802cb13b0f2a834b9210984af0a8428 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Wed, 10 May 2017 21:41:21 -0700 Subject: Revert "Documentation on how to write unit tests using auto-generated mocked stubs." --- doc/unit_testing.md | 175 ---------------------------------------------------- 1 file changed, 175 deletions(-) delete mode 100644 doc/unit_testing.md (limited to 'doc/unit_testing.md') diff --git a/doc/unit_testing.md b/doc/unit_testing.md deleted file mode 100644 index 380517fea5..0000000000 --- a/doc/unit_testing.md +++ /dev/null @@ -1,175 +0,0 @@ -# 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> - 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 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(); -``` - -- cgit v1.2.3