0

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

#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。)

4

2 回答 2

1

您的宏的问题在于,当它展开时,它会展开为一行。虽然我不知道你正在使用的测试框架,但很明显宏做了一些与此相当的事情:

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 ...);
// ...
于 2016-07-22T05:36:55.420 回答
1

@Aconcagua 是绝对正确的。我的解决方案类似,但使用函子(正如@R Sahu 建议的那样 --- C++ Single function pointer for all template instances

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>();
}
于 2016-07-22T15:54:22.323 回答