c++ 单元测试 doctest 笔记

相关资料

github: https://github.com/onqtam/doctest

中文文档: C++单元测试工具——doctest

hello world

源码

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include <doctest/doctest.h>

int add(int a, int b)
{
    return a + b;
}

TEST_CASE("test_add")
{
    CHECK(3 == add(1, 2));
}

测试结果

[laolang@localhost doctest-study]$ ./build/dist/test/cm_test 
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
[doctest] test cases: 1 | 1 passed | 0 failed | 0 skipped
[doctest] assertions: 1 | 1 passed | 0 failed |
[doctest] Status: SUCCESS!
[laolang@localhost doctest-study]$ 

常规使用方法

按照 doctest 官方建议, 只需要在测试程序主文件添加一个 #definedoctest.h 即可, 将测试代码移动到其他文件, 测试代码包含 doctest.h 即可.

注意: #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN只能有一个, 不要重复定义

[laolang@localhost test]$ ls -l
总用量 12
-rw-rw-r--. 1 laolang laolang 1359 6月  21 03:37 CMakeLists.txt
-rw-rw-r--. 1 laolang laolang   72 6月  21 03:50 testmain.cpp
-rw-rw-r--. 1 laolang laolang  128 6月  21 03:51 test_util_calc.cpp
[laolang@localhost test]$ cat testmain.cpp # 测试程序主文件只需要两行代码
     1	#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
     2	#include <doctest/doctest.h>
[laolang@localhost test]$ cat test_util_calc.cpp # 将测试逻辑放在其他文件
     1	#include <doctest/doctest.h>
     2	
     3	int add(int a, int b)
     4	{
     5	    return a + b;
     6	}
     7	
     8	TEST_CASE("test_add")
     9	{
    10	    CHECK(3 == add(1, 2));
    11	}

[laolang@localhost test]$ 

TEST_CASE , SUBCASE 与 TEST_SUITE

简单使用

  • TEST_CASE是测试用例, 可以包含多个SUBCASE
  • TEST_SUITE也就是测试套件, 可以包含多个TEST_CASE

使用 TEST_SUITE 的好处是资源只需要初始化一次

代码

源码

// calc.h 
#ifndef _CM_UTIL_CALC_H_
#define _CM_UTIL_CALC_H_

class Calc
{
public:
    Calc();
    ~Calc();
    Calc(const Calc &) = delete;
    Calc &operator=(const Calc &) = delete;

    int add(int x, int y);
};

#endif

// calc.cpp
#include <iostream>

#include "cm/util/calc.h"

Calc::Calc()
{
    std::cout << "calc constructor" << std::endl;
}
Calc::~Calc()
{
    std::cout << "calc destructor" << std::endl;
}

int Calc::add(int x, int y)
{
    return x + y;
}

测试代码

#include <iostream>
#include <string>
#include <memory>

#include <fmt/core.h>
#include <doctest/doctest.h>

#include "cm/util/calc.h"

TEST_SUITE("test_calc")
{
    auto calc = std::make_unique<Calc>();

    TEST_CASE("test_calc_add_1")
    {
        SUBCASE("test_calc_add_1_1")
        {
            CHECK(3 == calc->add(1, 2));
        }
        SUBCASE("test_calc_add_1_2")
        {
            CHECK(3 == calc->add(1, 2));
        }
    }

    TEST_CASE("test_calc_add_2")
    {
        int expected = 4;
        int actual = calc->add(1, 2);
        CHECK_MESSAGE(expected == actual, fmt::format("add2 测试失败. 期望:{}, 实际:{}", expected, actual));
    }
}

效果

[laolang@localhost doctest-study]$ ./build/dist/test/cm_test 
calc constructor # Calc 构造函数
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
/home/laolang/code/doctest-study/test/test_util_calc.cpp:26:
TEST SUITE: test_calc
TEST CASE:  test_calc_add_2

/home/laolang/code/doctest-study/test/test_util_calc.cpp:30: ERROR: CHECK( expected == actual ) is NOT correct!
  values: CHECK( 4 == 3 )
  logged: add2 测试失败. 期望:4, 实际:3

===============================================================================
[doctest] test cases: 2 | 1 passed | 1 failed | 0 skipped
[doctest] assertions: 3 | 2 passed | 1 failed |
[doctest] Status: FAILURE!
calc destructor # Calc 析构函数
[laolang@localhost doctest-study]$ 

装饰器

可以实现一些附加功能, 比如超时测试, 单位为秒

TEST_SUITE("not longer than 500ms" * doctest::timeout(0.5)) {
    TEST_CASE("500ms limit") {
        // asserts
    }
    TEST_CASE("200ms limit" * doctest::timeout(0.2)) {
        // asserts
    }
}

命令行参数

doctest 带了一个命令行管理, 运行时加上--help就可以查看所有命令行选项的使用帮助, 这些命令行选项没有多余的, 比较有用的比如

  • -ltc: 列出所有的 test case
  • -lts: 列出所有的 test suite
  • -tc: 根据名称过滤 test case, 例如 -tc="math"
  • -ts: 与 -tc 想通, 不过过滤的时 test suite
  • -tce: 跳过指定 test case
  • -tse: 跳过指定 test suite

例如

[laolang@localhost doctest-study]$ ./build/dist/test/cm_test --ltc # 列出所有 test case
calc constructor
[doctest] listing all test case names
===============================================================================
test_calc_add_1
test_calc_add_2
===============================================================================
[doctest] unskipped test cases passing the current filters: 2
calc destructor
[laolang@localhost doctest-study]$ ./build/dist/test/cm_test --lts # 列出所有 test suite
calc constructor
[doctest] listing all test suites
===============================================================================
test_calc
===============================================================================
[doctest] unskipped test cases passing the current filters: 2
[doctest] test suites with unskipped test cases passing the current filters: 1
calc destructor
[laolang@localhost doctest-study]$ ./build/dist/test/cm_test --tc="test_calc_add*" # 执行所有 test_calc_add 开头的 test case
calc constructor
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
/home/laolang/code/doctest-study/test/test_util_calc.cpp:26:
TEST SUITE: test_calc
TEST CASE:  test_calc_add_2

/home/laolang/code/doctest-study/test/test_util_calc.cpp:30: ERROR: CHECK( expected == actual ) is NOT correct!
  values: CHECK( 4 == 3 )
  logged: add2 测试失败. 期望:4, 实际:3

===============================================================================
[doctest] test cases: 2 | 1 passed | 1 failed | 0 skipped
[doctest] assertions: 3 | 2 passed | 1 failed |
[doctest] Status: FAILURE!
calc destructor
[laolang@localhost doctest-study]$ ./build/dist/test/cm_test --tce="test_calc_add_2" # 不执行 test_calc_add_2
calc constructor
[doctest] doctest version is "2.4.11"
[doctest] run with "--help" for options
===============================================================================
[doctest] test cases: 1 | 1 passed | 0 failed | 1 skipped
[doctest] assertions: 2 | 2 passed | 0 failed |
[doctest] Status: SUCCESS!
calc destructor
[laolang@localhost doctest-study]$ 

测试报告

doctest 支持三种输出, 分别是 console, junit, xml, 默认值为 console,可以使用-r选项设置输出报告格式. 使用 -o 选项输出到文件

例如上面的测试代码使用 test -r=xml -o=result.xml 结果如下

<?xml version="1.0" encoding="UTF-8"?>
<doctest binary="./cm_test" version="2.4.11">
  <Options order_by="file" rand_seed="0" first="0" last="4294967295" abort_after="0" subcase_filter_levels="2147483647" case_sensitive="false" no_throw="false" no_skip="false"/>
  <TestSuite name="test_calc">
    <TestCase name="test_calc_add_1" filename="/home/laolang/code/doctest-study/test/test_util_calc.cpp" line="14">
      <SubCase name="test_calc_add_1_1" filename="/home/laolang/code/doctest-study/test/test_util_calc.cpp" line="16">
      </SubCase>
      <SubCase name="test_calc_add_1_2" filename="/home/laolang/code/doctest-study/test/test_util_calc.cpp" line="20">
      </SubCase>
      <OverallResultsAsserts successes="2" failures="0" test_case_success="true"/>
    </TestCase>
    <TestCase name="test_calc_add_2" filename="/home/laolang/code/doctest-study/test/test_util_calc.cpp" line="26">
      <Expression success="false" type="CHECK" filename="/home/laolang/code/doctest-study/test/test_util_calc.cpp" line="30">
        <Original>
          expected == actual
        </Original>
        <Expanded>
          4 == 3
        </Expanded>
        <Info>
          add2 测试失败. 期望:4, 实际:3
        </Info>
      </Expression>
      <OverallResultsAsserts successes="0" failures="1" test_case_success="false"/>
    </TestCase>
  </TestSuite>
  <OverallResultsAsserts successes="2" failures="1"/>
  <OverallResultsTestCases successes="1" failures="1" skipped="0"/>
</doctest>

这个输出过滤了 std::cout 等输出, 并且包含了所有必要的信息, 比如test case 的名称, 断言失败的原因, 断言数量等, 可以使用python+Bootstrap写一个转换器, 把 xml 文件转换为 html, 比如参考这里Extent Reports使用示例

posted @ 2023-06-21 04:52  laolang2016  阅读(345)  评论(0编辑  收藏  举报