库api使用CRTP和谷歌测试框架

时间:2018-06-18 06:37:43

标签: c++ unit-testing templates googletest crtp

我有一个使用CRTP

的模板库
Object { status: "ok", jokes: Array[10], ref-id: 11 }

现在,用户使用// Base configuration to be derived from. struct TemplateBaseConfig { static constexpr auto config_1 = true; static constexpr auto config_2 = false; static constexpr auto config_val_3 = 42; }; template <typename Derived_, typename DerivedConfig_, typename... Widgets> struct TemplateBaseClass { std::tuple<Widgets...> widgets; // Lots of functions and logic etc. void function_1() { if constexpr (DerivedConfig_::config_1) { // do something } static_cast<Derived_*>(this)->function_2(); } void function_2() { /*do something*/ } }; 作为:

TemplateBaseClass

我通过继承// Make a custom config. struct DerivedConfig : TemplateBaseConfig { static constexpr auto config_1 = false; }; struct DerivedClass : TemplateBaseClass<DerivedClass, DerivedConfig, Widget1, Widget2> { void function_2() { /* override base logic */ } }; 来编写TemplateBaseClass的单元测试。

MockTemplateBaseClassInterface

要为给定的template <typename TestingClass, typename TestingClassConfig, typename... W> struct MockTemplateBaseClassInterface : TemplateBaseClass<TestingClass, TestingClassConfig, W> { // Mock some internal functions which always need to be mocked. MOCK(internal_function_1); MOCK(internal_function_2); // Some custom setup for unittests like override the log dir void function_2() { Parent::function_2(); MakeLogDir(); } // Parent is this guy's base class. } 编写单元测试,因此我从DerivedConfig创建一个派生类:

MockTemplateBaseClassInterface

然后我在fixture类中使用ClassToTestFunction1 : MockTemplateBaseClassInterface<ClassToTestFunction1, TestConfig1, MockWidget> { // Write more mock functions if required. MOCK(function3); }; 来编写单元测试。

这可以完美地测试ClassToTestFunction1的任何内容,因为我可以给出任何配置,在需要时编写我自己的模拟。

现在,如果我必须为最终用户公开这个测试框架,以便他可以为TemplateBaseClass编写测试,那么前进的方向应该是什么?

我可以要求用户执行以下操作:

DerivedClass

但现在用户无法真正模仿 struct DerivedClass : #ifdef _DOING_TESTING_ MockTemplateBaseClassInterface<DerivedClass, DerivedConfig, Widget1, Widget2> #else TemplateBaseClass<DerivedClass, DerivedConfig, Widget1, Widget2> #endif { void function_2() { /* override base logic */ } }; 的功能。

用户希望测试他的TemplateBaseClass。但如果function_2()使用function_2()的某些功能,他会想要嘲笑它。我的意思是,这是整个单元测试的重点吗?

1 个答案:

答案 0 :(得分:0)

你的方法过于复杂。

TemplateBaseClass类模板不需要将Derived_DerivedConfig作为特定内容 - 例如从TemplateBaseConfig派生。

只是定义TemplateBaseClass是完全特定于测试的类 - 我的意思是 - 单独测试TemplateBaseClass到项目中的任何其他类:

struct TemplateBaseClass_TestConfig
{
    bool config_1;
    bool config_2;
    int config_val_3;
};
struct TemplateBaseClass_TestDerived
{
    MOCK_METHOD0(function_1, void());
    MOCK_METHOD0(function_2, void());
};


class TemplateBaseClassTestSuite : public ::testing::Test
{
public:
     using ClassUnderTest = TemplateBaseClass<TemplateBaseClass_TestConfig
                                              TemplateBaseClass_TestDerived
                                              /* specify some extra WidgetMocks if needed */
                                              >;
      ClassUnderTest  objectUnderTest{};
      TemplateBaseClass_TestDerived& derivedMock = objectUnderTest;   
};

然后你的测试就像这样简单:

TEST_F(TemplateBaseClassTestSuite, function1CallsDerivedFunction1IfConfig1Allows)
{
    objectUnderTest.config_1 = true;
    EXPECT_CALL(derivedMock, function_1());
    objectUnderTest.function_1();
    // of course the above is just example - I have no idea if this is correct behaviour for your real class
}

所以 - 如您所见 - 没有必要使用您的测试机制污染客户端类。测试应该是透明的。