实现一个轻量级 C++ 单元测试框架

如何实现一个轻量级 C++ 单元测试框架——MiniTest

在 C++ 项目开发中,单元测试 是保证代码质量的重要手段。尽管 Google TestBoost.Test 是流行的测试框架,但它们较为复杂,适用于大型项目。如果你想要一个轻量级、易于理解的 C++ 单元测试框架,本文将带你实现 MiniTest,一个仅需几个头文件即可完成的 C++ 单元测试框架。


📌 为什么需要自定义单元测试框架?

在 C++ 项目中,单元测试通常需要具备以下能力:

  1. 断言机制:检查某个条件是否成立,失败时给出错误提示。
  2. 测试管理:能够注册、存储和执行多个测试用例。
  3. 灵活运行:支持运行所有测试,也支持运行单个测试。
  4. 跨平台支持:能够在不同平台(Windows、Linux、Mac)上运行。

既然 gtestBoost.Test 已经很强大,为什么还要自己实现一个框架呢?

  • 轻量级:无需引入外部库,减少依赖。
  • 更好理解:帮助你深入理解单元测试框架的实现原理。
  • 定制化:可以根据需求自由扩展。

📌 MiniTest 框架的组成

MiniTest 框架由 三个主要组件 组成:

  1. 断言宏 (TestAssert.hpp):提供 ASSERT_TRUE, ASSERT_EQ, ASSERT_THROW 等基本断言。
  2. 测试管理 (TestFramework.hpp):负责存储、注册和执行测试。
  3. 测试代码 (main.cpp):用户编写测试用例,并运行测试。

我们将在下面的部分详细介绍它们的作用,并给出代码实现。


📌 1. 断言宏:TestAssert.hpp

💡 作用

TestAssert.hpp 负责提供基本的断言机制,用于检查测试是否通过。例如:

  • ASSERT_TRUE(condition):如果 condition 不是 true,测试失败。
  • ASSERT_EQ(expected, actual):如果 expected != actual,测试失败。
  • ASSERT_THROW(statement, exception_type):检查 statement 是否抛出 exception_type

📌 代码示例

#ifndef TEST_ASSERT_HPP
#define TEST_ASSERT_HPP

#include <iostream>
#include <stdexcept>

// 断言:检查是否为真
#define ASSERT_TRUE(condition) \
    do { \
        if (!(condition)) { \
            throw std::runtime_error("Assertion failed: " #condition); \
        } \
    } while (0)

// 断言:检查是否为假
#define ASSERT_FALSE(condition) ASSERT_TRUE(!(condition))

// 断言:检查两个值是否相等
#define ASSERT_EQ(expected, actual) \
    do { \
        if ((expected) != (actual)) { \
            throw std::runtime_error("Assertion failed: " #expected " != " #actual); \
        } \
    } while (0)

// 断言:检查两个值是否不相等
#define ASSERT_NE(expected, actual) ASSERT_TRUE((expected) != (actual))

// 断言:检查是否抛出指定的异常
#define ASSERT_THROW(statement, exception_type) \
    do { \
        bool bCaught = false; \
        try { \
            statement; \
        } catch (const exception_type&) { \
            bCaught = true; \
        } catch (...) { \
            throw std::runtime_error("Assertion failed: Unexpected exception caught."); \
        } \
        if (!bCaught) { \
            throw std::runtime_error("Assertion failed: Exception of type " #exception_type " not thrown."); \
        } \
    } while (0)

#endif // TEST_ASSERT_HPP

📌 2. 测试管理:TestFramework.hpp

💡 作用

  • 注册测试:存储所有测试函数。
  • 执行测试:可以运行所有测试,也可以只运行指定测试。
  • 输出结果:测试成功/失败的日志信息。

📌 代码示例

#ifndef TEST_FRAMEWORK_HPP
#define TEST_FRAMEWORK_HPP

#include <vector>
#include <functional>
#include <string>
#include <iostream>

// 单元测试框架
class TestFramework {
public:
    // 注册测试
    static void RegisterTest(const std::string& testName, std::function<void()> testFunc) {
        GetTests().push_back({testName, testFunc});
    }

    // 运行所有测试
    static void RunAllTests() {
        int passed = 0;
        int failed = 0;

        for (const auto& test : GetTests()) {
            try {
                test.func();
                std::cout << "[PASS] " << test.name << std::endl;
                ++passed;
            } catch (const std::exception& ex) {
                std::cerr << "[FAIL] " << test.name << " - " << ex.what() << std::endl;
                ++failed;
            }
        }

        std::cout << "===================================" << std::endl;
        std::cout << "Total: " << (passed + failed) << ", Passed: " << passed << ", Failed: " << failed << std::endl;
    }

    // 运行指定名称的测试
    static void RunTestByName(const std::string& testName) {
        for (const auto& test : GetTests()) {
            if (test.name == testName) {
                try {
                    test.func();
                    std::cout << "[PASS] " << test.name << std::endl;
                } catch (const std::exception& ex) {
                    std::cerr << "[FAIL] " << test.name << " - " << ex.what() << std::endl;
                }
                return;
            }
        }
        std::cerr << "[ERROR] Test '" << testName << "' not found." << std::endl;
    }

private:
    struct TestCase {
        std::string name;
        std::function<void()> func;
    };

    static std::vector<TestCase>& GetTests() {
        static std::vector<TestCase> tests;
        return tests;
    }
};

// 定义 TEST 宏,自动注册测试
#define TEST(test_name) \
    void test_name(); \
    struct TestRegister_##test_name { \
        TestRegister_##test_name() { \
            TestFramework::RegisterTest(#test_name, test_name); \
        } \
    }; \
    static TestRegister_##test_name g_register_##test_name; \
    void test_name()

#endif // TEST_FRAMEWORK_HPP

📌 3. 编写测试代码

📌 main.cpp:用于调用 RunAllTests()RunTestByName() 运行测试。

📌 代码示例

#include <iostream>
#include "include/TestAssert.hpp"
#include "include/TestFramework.hpp"

// 数学加法测试
TEST(TestAddition) {
    int a = 2;
    int b = 3;
    ASSERT_EQ(a + b, 5);
}

// 逻辑测试
TEST(TestBoolean) {
    bool flag = true;
    ASSERT_TRUE(flag);
}

// 失败示例(演示 `ASSERT_TRUE` 失败的情况)
TEST(TestFailure) {
    bool flag = false;
    ASSERT_TRUE(flag);  // 这个测试会失败
}

// 异常测试
TEST(TestException) {
    ASSERT_THROW(throw std::runtime_error("error"), std::runtime_error);
}

int main() {
    TestFramework::RunAllTests();  // 运行所有测试
    std::cout << "===================================" << std::endl;
    TestFramework::RunTestByName("TestAddition"); // 只运行 TestAddition
    return 0;
}

📌 运行 MiniTest

1. 编译

g++ -std=c++20 main.cpp -o MiniTest

2. 运行所有测试

./MiniTest

3. 运行单个测试

./MiniTest TestAddition

📌 通过 CLion 运行

文件组织结构

/project_root
│── /tests
│   │── test_example.cpp       # 测试代码
│   │── test_math.cpp          # 另一个测试代码
│── /include
│   │── TestFramework.hpp      # 测试框架
│   │── TestAssert.hpp         # 断言宏
│── /src
│   │── TestFramework.cpp      # 测试框架的实现(如果有)
│── main.cpp                   # 运行所有测试的 main 函数

编写 CMakeLists.txt

cmake_minimum_required(VERSION 3.30)
project(MiniTest)

set(CMAKE_CXX_STANDARD 20)

# 确保 MSVC 使用 UTF-8
add_compile_options(/utf-8)

include_directories(include)
add_executable(MiniTest main.cpp)

运行

在这里插入图片描述


📌 总结

轻量级 C++ 单元测试框架
支持 ASSERT_TRUE, ASSERT_EQ, ASSERT_THROW
支持运行所有测试 or 运行特定测试
代码结构清晰,易于扩展

🚀 下一步实现

  • 支持 Setup / Teardown
  • 支持性能测试
  • 支持测试分组
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值