使用 Catch 框架测试 C++ 模板 class

Test C++ template class using Catch framework

我正在寻找使用 Catch 测试模板化 class 的好方法。我有一些几乎可以用的东西:

#define RUN_ALL(fn, params)  \
fn<uint8_t, bool>(params);  \
fn<uint8_t, char>(params);  \
fn<uint16_t, bool>(params); \
fn<uint16_t, char>(params); \
fn<uint32_t, bool>(params); \
fn<uint32_t, char>(params); \
fn<uint64_t, bool>(params); \
fn<uint64_t, char>(params);

template<typename A, typename B>
void test_number_one() {
   REQUIRE(...)
} 

TEST_CASE("Foo::Foo() works nicely", "[SmallGraph]") {
  RUN_ALL(test_number_one)
}

此设置只会 运行 直到第一次失败,这很好,因为很可能所有 8 个案例都会以相同的方式失败。但是,如果知道发生故障时正在使用哪组模板参数,那将是一件好事。我的想法是这样做:

#define RUN_ALL_P(fn, params)  \
INFO("Testing <uint8_t, bool>"); \
fn<uint8_t, bool>(params);  \
INFO("Testing <uint8_t, char>"); \
fn<uint8_t, char>(params);  \
INFO("Testing <uint16_t, bool>"); \
fn<uint16_t, bool>(params); \
...

但是,我不能在 RUN_ALL 中使用多个 INFO,因为这样做会生成具有重复标识符的代码。

FOO.cpp:270:3: error: redefinition of 'scopedMessage270'
  RUN_ALL(test_number_one);

RUN_ALL(test_number_one) 出现在第 270 行。)

对于不需要所有测试函数都具有相同签名的解决方法有什么想法吗?

(我也欢迎指点有关使用 CATCH 测试模板代码的文章,以及有关如何搜索此类文章而不会得到一堆关于一般异常处理的结果的建议——即 try/catch .)

你的宏的问题是,当它被展开时,它被展开成一行。虽然我不知道你使用的测试框架,但很明显宏做了类似这样的事情:

struct M { M(char* msg) { puts(msg); } }; // just an example class...
#define INFO(m) M scopedMessage##__line__(msg)

因此,如果您在第 270 行使用宏 RUN_ALL,您将获得多个 scopedMessage270 实例...

您可以通过用模板替换宏来解决该问题。不幸的是,您不能将其与模板函数一起使用,因此您也必须制作测试用例模板 类:

template <template <typename T, typename TT > class Test >
struct All
{
    template <typename ... Parameters>
    static void run(Parameters ... parameters)
    {
        Test<uint8_t, bool>::run(parameters ...);
        Test<uint8_t, char>::run(parameters ...);
        Test<uint16_t, bool>::run(parameters ...);
        Test<uint16_t, char>::run(parameters ...);
        Test<uint32_t, bool>::run(parameters ...);
        Test<uint32_t, char>::run(parameters ...);
        Test<uint64_t, bool>::run(parameters ...);
        Test<uint64_t, char>::run(parameters ...);
    }
};

template<typename A, typename B>
struct test_number_one
{
    static void run()
    {
        // log test name
        // run the test
    }
};
template<typename A, typename B>
struct test_number_two
{
    static void run(int n)
    {
        // log test name and parameter value
        // run the test
    }
};

int main(int argc, char* argv[])
{
    All<test_number_one>::run();
    All<test_number_two>::run(12);
    All<test_number_two>::run(10);
}

与现在一样,在模板中,所有代码行都保留在单独的行中,您可以随意放置在任何日志记录之间:

template <typename ... Parameters>
static void run(Parameters ... parameters)
{
    INFO("uint8_t, bool");
    Test<uint8_t, bool>::run(parameters ...);
    INFO("uint8_t, char");
    Test<uint8_t, char>::run(parameters ...);
// ...

@Aconcagua 完全正确。我的解决方案是相似的,但使用仿函数(如@R Sahu 所建议的---

template<template<typename, typename> class TestFunctor, typename... Parameters>
void testAllTypes(Parameters... parameters) {

  INFO("Testing <uint8_t, bool>");
  TestFunctor<uint8_t, bool>()(parameters...);
  INFO("Testing <uint8_t, char>");
  TestFunctor<uint8_t, char>()(parameters...);

  // ...
}

template<typename A, typename B>
struct testDefaultConstructor {
  void operator()() {
    mallGraph<A, B> sg;
    REQUIRE(sg.numVertices() == 0);
    REQUIRE_FALSE(sg.edgecountIsValid());
    REQUIRE_FALSE(sg.adjacencyMatrixIsValid());        
  }
};


TEST_CASE("SmallGraph::SmallGraph() initializes instance data as expected", "[SmallGraph]") {
  testAllTypes<testDefaultConstructor>();
}