引言:为什么我们写bug,然后又英勇地修复它们?
\n\n你是否曾经写过“看起来还行”的代码,但三天后却发现一个导致一切崩溃的bug?如果是——欢迎加入俱乐部。如果不是——你要么是C++大神,要么是还没上过生产环境。
\n\n测试驱动开发(TDD)不是程序员的时尚节食法。它是一种颠覆代码编写过程的纪律。与其先写逻辑,然后拼命想如何测试它,我们反其道而行之:先写测试,再写代码。听起来很疯狂?也许吧。但正是这样,才能写出不羞于展示给同事、也不会在周五晚上崩溃的代码。
\n\n在本文中,我们将通过实际示例剖析C++中的TDD:使用红-绿-重构循环编写一个简单的斐波那契数库。你将看到TDD如何不仅帮助避免bug,还能设计接口。
\n\n什么是TDD,为什么C++没有它就像没有刹车?
\n\nTDD(测试驱动开发)是一种开发方法论,先编写一个注定失败的测试(Red),然后编写最少的代码使测试通过(Green),最后进行重构(Refactor)以改进结构而不改变行为。
\n\n为什么这对C++特别重要?因为C++是一种容易搬起石头砸自己脚的语言:未定义行为、内存泄漏、错误的类型转换。TDD自动创建了一个安全网,在编译和执行阶段捕获错误。
\n\n红-绿-重构循环:它是如何工作的
\n\n- \n
- Red:编写一个应该失败的测试。这是我们对代码的“要求”。 \n
- Green:编写最少的代码使测试通过。不要过度设计! \n
- Refactor:改进代码(消除重复、简化逻辑)——测试保持绿色。 \n
听起来简单?是的。但在实践中需要纪律。让我们试试看。
\n\nC++中TDD的工具:Google Test vs Catch2
\n\n在编写代码之前,我们选择一个测试框架。两个主要候选:
\n\n- \n
- Google Test——功能强大、流行,带有大量宏。非常适合大型项目。 \n
- Catch2——轻量、现代,语法方便。非常适合教学示例和微服务。 \n
在本文中,我们将使用Catch2(v3),因为它简洁且不需要额外配置。但如果你更喜欢Google Test——TDD的原则是一样的。
\n\n实践:为斐波那契数编写TDD
\n\n假设我们需要编写一个函数 fibonacci(int n),返回第n个斐波那契数(第0个=0,第1个=1,第2个=1,第3个=2,依此类推)。
步骤1:Red——编写一个不通过的测试
\n\n创建文件 test_fib.cpp:
#define CATCH_CONFIG_MAIN\n#include <catch2/catch_test_macros.hpp>\n\n// 函数尚未声明——这没问题\nint fibonacci(int n);\n\nTEST_CASE("Fibonacci numbers", "[fib]") {\n REQUIRE(fibonacci(0) == 0);\n REQUIRE(fibonacci(1) == 1);\n REQUIRE(fibonacci(2) == 1);\n REQUIRE(fibonacci(10) == 55);\n}\n\n尝试编译——得到链接错误。测试为RED。很好!
\n\n步骤2:Green——编写最少的代码使测试通过
\n\n创建文件 fib.cpp:
int fibonacci(int n) {\n if (n <= 1) return n;\n return fibonacci(n - 1) + fibonacci(n - 2);\n}\n\n编译并运行测试。它们通过了?是的,但这效率不高。然而我们的目标是Green,而不是完美。暂时保持这样。
\n\n