测试用例
测试用例
虽然 doctest 完全支持包含测试用例方法的基于类的传统 xUnit 样式, 但这不是首选样式。相反,doctest 提供了一种强大的机制,用于在测试用例中嵌套子用例。有关更详细的讨论和示例,请参阅 tutorial。
测试用例和子用例在实践中非常容易使用:
- TEST_CASE(
test name) - SUBCASE(
subcase name)
test name 和 subcase name 是自由格式、带引号的字符串。测试名称在 doctest 可执行文件中不必是唯一的。它们也应该是字符串文字。
可以借助TEST_CASE_CLASS()在 C++17 的类主体内编写测试用例 - 就像TEST_CASE()一样使用 - 使测试类的私有部分变得 更容易。
请记住,即使 doctest 是线程安全 - 使用子用例只能在主测试运行程序线程中完成。
测试用例也可以参数化 - 请参阅文档
可以通过使用命令行 来过滤测试用例和子用例
BDD 风格的测试用例
除了doctest采用经典的测试用例风格之外,doctest还支持一种替代语法,允许将测试编写为“可执行规范”(的早期目标之一行为驱动开发)。这组宏
映射到TEST_CASE和SUBCASE,并带有一些内部支持,使它们更易于使用。
- SCENARIO(
scenario name)
该宏映射到TEST_CASE并以相同的方式工作,只是测试用例名称将以Scenario:为前缀
- SCENARIO_TEMPLATE(
scenario name, type,list of types)
该宏映射到``TEST_CASE_TEMPLATE```并以相同的方式工作,除了测试用例名称将以Scenario:为前缀
- SCENARIO_TEMPLATE_DEFINE(
scenario name, type, id )
该宏映射到``TEST_CASE_TEMPLATE_DEFINE```并以相同的方式工作,除了测试用例名称将以Scenario:为前缀
- GIVEN( something )
- WHEN( something )
- THEN( something )
这些宏映射到SUBCASE,除了子案例名称是分别以given:、when:或then:为前缀的 something。
- AND_WHEN( something )
- AND_THEN( something )
与``WHEN 和THEN```` 类似,只是前缀以and开头。它们用于将 WHEN 和 THEN 链接在一起。
当使用这些宏中的任何一个时,控制台报告器会识别它们并格式化测试用例标题,以使Givens、Whens和Thens对齐以帮助可读性。
除了控制台报告器中的附加前缀和格式之外,这些宏的行为与TEST_CASE和SUBCASE完全相同。因此,没有任何东西可以强制执行这些宏的正确排序 - 这取决于程序员!
请注意,使用 --test-case=<filters> 命令行选项(或 - -subcase=<filters>) 您还必须传递前缀 Scenario:。
Test fixtures
尽管 doctest 允许您将测试作为测试用例中的子用例分组在一起,但有时使用更传统的测试装置将它们分组仍然很方便。 doctest 也完全支持这一点。您将测试夹具定义为一个简单的结构:
class UniqueTestsFixture {
private:
static int uniqueID;
protected:
DBConnection conn;
public:
UniqueTestsFixture() : conn(DBConnection::createConnection("myDB")) {}
protected:
int getID() {
return ++uniqueID;
}
};
int UniqueTestsFixture::uniqueID = 0;
TEST_CASE_FIXTURE(UniqueTestsFixture, "Create Employee/No Name") {
REQUIRE_THROWS(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), ""));
}
TEST_CASE_FIXTURE(UniqueTestsFixture, "Create Employee/Normal") {
REQUIRE(conn.executeSQL("INSERT INTO employee (id, name) VALUES (?, ?)", getID(), "Joe Bloggs"));
}
这里的两个测试用例将创建唯一命名的 UniqueTestsFixture 派生类,因此可以访问getID()受保护方法和conn成员变量。这确保了两个测试用例都能
够使用相同的方法(DRY 原则)创建 DBConnection,并且创建的任何 ID 都是唯一的,因此测试执行的顺序并不重要。
Test suites
测试用例可以分为测试套件。这是通过TEST_SUITE()或TEST_SUITE_BEGIN()/TEST_SUITE_END()完成的。
例如:
TEST_CASE("") {} // not part of any test suite
TEST_SUITE("math") {
TEST_CASE("") {} // part of the math test suite
TEST_CASE("") {} // part of the math test suite
}
TEST_SUITE_BEGIN("utils");
TEST_CASE("") {} // part of the utils test suite
TEST_SUITE_END();
TEST_CASE("") {} // not part of any test suite
然后可以在过滤器的帮助下执行特定测试套件中的测试用例 - 查看命令行
装饰器
测试用例可以用附加属性装饰,如下所示:
TEST_CASE("name"
* doctest::description("shouldn't take more than 500ms")
* doctest::timeout(0.5)) {
// asserts
}
可以同时使用多个装饰器。这些是当前支持的装饰器:
skip(bool = true)- 标记要跳过执行的测试用例 - 除非使用--no-skip选项no_breaks(bool = true)- 测试用例中的断言不会闯入调试器 - 与may_fail/should_fail/expected_failures结合使用很有用no_output(bool = true)- 测试用例中的断言没有输出 - 与may_fail/should_fail/expected_failures结合使用很有用may_fail(bool = true)- 如果任何给定的断言失败(但仍然报告它),则测试不会失败 - 这对于标记正在进行的工作很有用,或者您不想立即修复但仍想在测试中跟踪的已知问题should_fail(bool = true)- 就像 ```may_fail()```` 但如果通过则测试失败 - 如果您想收到通知,这可能很有用意外或第三方修复expected_failures(int)- 定义测试用例中预期失败的断言数量 - 当失败断言的数量与声明的预期失败数量不同时报告为失败timeout(double)- 如果其执行超过此限制(以秒为单位),则测试用例失败 - 但不会终止它 - 这需要子进程支持test_suite("name")- 可用于测试用例来覆盖(或只是设置)它们所在的测试套件description("text")- 测试用例的描述
装饰器采用的值是在注册测试用例时(在全局初始化期间)计算的 - 在进入 main() 之前,而不是在运行它们之前。
装饰器也可以应用于测试套件块,并且该块中的所有测试用例都继承它们:
TEST_SUITE("some TS" * doctest::description("all tests will have this")) {
TEST_CASE("has a description from the surrounding test suite") {
// asserts
}
}
TEST_SUITE("some TS") {
TEST_CASE("no description even though in the same test suite as the one above") {
// asserts
}
}
测试用例可以覆盖它们从周围测试套件继承的装饰器:
TEST_SUITE("not longer than 500ms" * doctest::timeout(0.5)) {
TEST_CASE("500ms limit") {
// asserts
}
TEST_CASE("200ms limit" * doctest::timeout(0.2)) {
// asserts
}
}
- 查看 subcases 和 BDD 示例
- 查看 断言宏示例 以了解如何使用测试套件
- 在预处理并包含标头后,从每个处理的 cpp 的顶部到底部注册测试,但 cpp 文件之间没有顺序。