# Framework-Less-Testing **Repository Path**: libbylg/Framework-Less-Testing ## Basic Information - **Project Name**: Framework-Less-Testing - **Description**: 一个极简的、开放定制的免框架的测试库 - **Primary Language**: C++ - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-05-05 - **Last Updated**: 2025-05-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Framework-Less Testing ### 介绍 我的字符串处理库 str(https://gitee.com/libbylg/str) 已经断断续续做了一年多了,现绝大多数功能都已实现了,是时候进入用例完善阶段了。然而,我发现我的每次改动,在编译的时候总是要花费很多时间,少则 2 分钟,多则十几分钟,简直忍无可忍(本来牺牲业余时间已经够痛苦了)。 我先后尝试了 Catch2 和 googletest 这两个框架,试过了 Header-Only 模式、静态库模式、动态库模式等多种方式集成。然而,我发现哪种方式,测试用例改动后的编译速度都很慢。 经过一通分析,我发现主要原因就是 Catch2 和 googletest 的头文件编译实在太慢了。为了验证这个结论,我特意不用任何测试框架,而只是将他们直接放在 main 函数中调用,其实编译耗时至少可以减少一半。 这个现象促使我考虑:对于我的 str 库这种超轻量级库,是否类真有必要依赖或者使用 googletest、Catch2 这些重量级的测试框架? 于是,我脑子里冒出了无框架的测试(Framework-Less Testing)的想法 ,而实际效果也感觉很不错。 ### 安装教程 * 直接源码集成 Framework-Less Testing 只有一个极小的头文件 `testing.hpp`,你可以直接将这个文件的内容拷贝粘贴和自己的代码集成到一起,这是推荐的做法。 * 通过 git 获取源码 ```shell git clone https://gitee.com/libbylg/Framework-Less-Testing.git ``` ### 使用说明 * 定义用例 用法很简单,跟 googletest 很类似 TEST(, ) 用于定义并自动注册一个测试用例。参考下面的例子(更多例子,可参考:https://gitee.com/libbylg/str/tree/master/test) ```c++ #include "testing.hpp" #include "str.h" TEST(test_str, wildcmp_cstr) { SECTION("检查是否匹配通配符") { ASSERT_EQ(str::wildcmp("3bc12def33", "3*"), true); ASSERT_TRUE(str::wildcmp("3abc", "3abc")); ASSERT_FALSE(str::wildcmp("3abc", "")); } SECTION("空串场景") { ASSERT_TRUE(str::wildcmp("", "")); } } TEST(test_str, wildcmp_string_view) { SECTION("检查是否匹配通配符") { ASSERT_TRUE(str::wildcmp(std::string_view{"3bc12def33"}, std::string_view{"3*"})); ASSERT_TRUE(str::wildcmp(std::string_view{"3abc"}, std::string_view{"3abc"})); ASSERT_TRUE(str::wildcmp(std::string_view{"3"}, std::string_view{"?"})); } } ``` * 执行所有用例 大部分时候 main 函数基本就是个固定函数,可参考下面的代码。其中,`TESTING_TESTCACSE_CLASS::exec_all(...)` 是框架提供的默认执行所有测试用例的函数。 ```c++ #include "testing.hpp" int main(int argc, char* argv[]) { return TESTING_TESTCACSE_CLASS::exec_all(argc, argv); } ``` * 断言宏 断言宏也直接参考 googletest 框架提供了 `ASSERT` 系列和 `EXPECT` 系列两类,但只提供了最常见、最基本的几个(参考后面的列表),如果需要可以自行基于 `TESTING_ASSERT` 和 `TESTING_EXPECT` 扩展或者自定实现。 * 场景分组 用 `SECTION(xxx)` 来划分一个子测试场景,这有些类似 Catch2 的机制,但并不具有 Catch2 的 SECTION 的功能。但本框架并非是对 googletest 或者 Catch2 的精确模拟。 * 各种主要断言宏 | 名称&定义 | 含义 | | --- | --- | | `EXPECT_EQ(Expr1_, Expr2_)` |当 Expr1_ 等于 Expr2_时,满足断言条件,否则触发用例失败,但不中断用例执行 | | `ASSERT_EQ(Expr1_, Expr2_)` |当 Expr1_ 等于 Expr2_时,满足断言条件,否则触发用例失败,且终止用例 | | `EXPECT_TRUE(Expr1_)` |当 Expr1_ 的值为 true 时,满足断言条件,否则触发用例失败,但不中断用例执行 | | `ASSERT_TRUE(Expr1_)` |当 Expr1_ 的值为 true 时,满足断言条件,否则触发用例失败,且终止用例 | | `EXPECT_FALSE(Expr1_)` |当 Expr1_ 的值为 false 时,满足断言条件,否则触发用例失败,但不中断用例执行 | | `ASSERT_FALSE(Expr1_)` |当 Expr1_ 的值为 false 时,满足断言条件,否则触发用例失败,且终止用例 | ### 如何定制 Framework-Less Testing 的特色就是足够简单和定制方便,下面给出一些常见的定制诉求及其方法。 在了解如何定制前,作者有一些好的建议,希望对您有所帮助。但这些建议主要是处于可维护性的考虑,如果遵守的话,测试代码会更有利于维护,特别是在本库升级时。但这些建议并非是强制性的----如果你自己的因由,您完全可以不去遵守,我们也鼓励您这样做! * 建议1:不要直接修改 `testing.hpp`;这样,当 `testing.hpp` 有新版本时,升级会更容易; * 建议2:当需要定制时,最好用一个独立的文件包装 `testing.hpp`(参考下面的代码); ```c++ // my_custom_testing.h #ifndef MY_CUSTOM_TESTING_H #define MY_CUSTOM_TESTING_H // <前置定制> ... #include "testing.h" // <后置定制> ... #endif//MY_CUSTOM_TESTING_H ``` 下表为常见的可定制接口: | 名称&定义 | 定制位置 | 含义 | | --- | --- | --- | | `TEST(SuiteName_, CaseName_)` | 前置定制 | 测试用例定义 | | `TESTING_TESTCACSE_CLASS` | 前置定制 | 测试用例类 | | `TESTING_MAKE_ASSERT` | 前置定制 | 执行断言的函数 | | `TESTING_ASSERT` | 前置定制 | 断言宏底层封装,针对 ASSERT_xxx 系列断言宏 | | `TESTING_EXPECT` | 前置定制 | 断言宏底层封装,针对 EXPECT_xxx 系列断言宏 | | `SECTION(Desc_)` | 后置定制 | 测试段定义 | | `EXPECT_EQ` | 后置定制 | 断言宏 | | `ASSERT_EQ` | 后置定制 | 断言宏 | | `EXPECT_TRUE` | 后置定制 | 断言宏 | | `ASSERT_TRUE` | 后置定制 | 断言宏 | | `EXPECT_FALSE` | 后置定制 | 断言宏 | | `ASSERT_FALSE` | 后置定制 | 断言宏 | #### 定制断言宏 * 新增断言宏 这个比较简单,直接定义即可 * 修改现有的断言宏 当前只提供了下面几个宏,重定义的方法比较简单就是直接在 `<后置定制>` 这里先 `#undef xxx`,然后重新用 `#define xxx ...` 重新定义 #### 支持测试套的Setup&TearDown 这个有点难度。小型项目,通常就用不着这么复杂的东西,而是用 SECTION 分段就够了。但如果确实有必要,下面给出一个思路: * (1)单独定义一个宏代替 `TEST` 宏 * (2)从 `testing::testcase` 派生一个新类,并重定义 TESTCACSE_CLASS * (3)定义独立函数代替 `testcase::exec` 和 `exec_all` ,在 `testcase::exec` 中检查测试套是否已经执行,如果未执行过,就执行测试套的 Setup,TearDown 函数 #### 定制比较运算 直接重载 `==` 运算符。 #### 定制测试报表 参考 `exec_all` 函数自己实现一个代替。 #### 定制断言处理流程 定义独立函数代替 `testcase::exec` 和 `exec_all`。可参考 `testing.hpp` 中,setjmp 和 longjmp 两个函数调用的地方。这种场景最常见的是希望用异常机制来代替 setjmp 和 longjmp,避免测试过程中句柄泄露。 ### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request