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

Test C++ template class using Catch framework

本文关键字:C++ 测试 Catch 框架 使用      更新时间:2023-10-16

我正在寻找一种使用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)的结果的情况下搜索此类文章的建议。)

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

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所建议的——C++所有模板实例的单个函数指针)

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>();
}