template <class T> class TemplateUnderTest { T *t_; public: TemplateUnderTest(T *t) : t_(t) {} void SomeMethod() { t->DoSomething(); t->DoSomeOtherThing(); } }; struct MockT { void DoSomething() { // Some assertions here. } void DoSomeOtherThing() { // Some more assertions here. } }; class UnitTest { void Test1() { MockT mock; TemplateUnderTest<MockT> test(&mock); test.SomeMethod(); assert(DoSomethingWasCalled(mock)); assert(DoSomeOtherThingWasCalled(mock)); } };
friend
classes. template <class T> class TemplateUnderTest { T t_; friend class UnitTest; public: void SomeMethod() { t.DoSomething(); t.DoSomeOtherThing(); } }; class UnitTest { void Test2() { TemplateUnderTest<MockT> test; test.SomeMethod(); assert(DoSomethingWasCalled(test.t_)); // access guts assert(DoSomeOtherThingWasCalled(test.t_)); // access guts } };
UnitTest :: Test2
has access to the TemplateUnderTest body and can verify assertions on the internal copy of MockT. template <class Request, class Response> class CallData { grpc::ServerCompletionQueue *cq_; grpc::ServerContext context_; grpc::ServerAsyncResponseWriter<Response> responder_; // ... some more state public: using RequestType = Request; using ResponseType = Response; CallData(grpc::ServerCompletionQueue *q) : cq_(q), responder_(&context_) {} void HandleRequest(Request *req); // application-specific code Response *GetResponse(); // application-specific code };
ServerAsyncResponseWriter
and ServerContext
, for example.grpc :: ServerCompletionQueue
is passed to the constructor as an argument, but has no virtual functions. Only virtual destructor.grpc :: ServerContext
is created inside and has no virtual functions.CallDataTraits
template <class CallData> class CallDataTraits { using ServerCompletionQueue = grpc::ServerCompletionQueue; using ServerContext = grpc::ServerContext; using ServerAsyncResponseWriter = grpc::ServerAsyncResponseWrite<typename CallData::ResponseType>; };
/// Unit testable CallData template <class Request, class Response> class CallData { typename CallDataTraits<CallData>::ServerCompletionQueue *cq_; typename CallDataTraits<CallData>::ServerContext context_; typename CallDataTraits<CallData>::ServerAsyncResponseWriter responder_; // ... some more state public: using RequestType = Request; using ResponseType = Response; CallData(typename CallDataTraits::ServerCompletionQueue *q) : cq_(q), responder_(&context_) {} void HandleRequest(Request *req); // application-specific code Response *GetResponse(); // application-specific code };
/// In unit test code struct TestRequest{}; struct TestResponse{}; struct MockServerCompletionQueue{}; struct MockServerContext{}; struct MockServerAsyncResponseWriter{}; /// We want to unit test this type. using CallDataUnderTest = CallData<TestRequest, TestResponse>; /// A specialization of CallDataTraits for unit testing purposes only. template <> class CallDataTraits<CallDataUnderTest> { using ServerCompletionQueue = MockServerCompletionQueue; using ServerContext = MockServerContext; using ServerAsyncResponseWriter = MockServerAsyncResponseWrite; }; MockServerCompletionQueue mock_queue; CallDataUnderTest cdut(&mock_queue); // Now injected with mock types.
Source: https://habr.com/ru/post/457110/
All Articles