4

我正在尝试使用TDD使用 VS 2010 在 C++ 中编写西洋双陆棋游戏。

我已经设置了CxxTest来编写测试用例。

第一个要测试的类是

class Position
{
public:
...
...
bool IsSingleMoveValid(.....)
...
...
}

我想为函数IsSingleMoveValid()编写一个测试,我想测试应该证明该函数可以正常工作。不幸的是,要测试的案例太多了,即使我测试了几个案例,有些可能会逃脱。

你有什么建议?TDD 如何处理这些问题?

4

5 回答 5

8

一些指导方针:

  1. 测试常规案例。在您的问题中:测试您知道有效的合法动作。您可以采用简单的方法,只使用少量测试用例,或者您可以编写一个循环,生成所有可能在您的应用程序中发生的合法移动并测试它们。
  2. 测试边界情况。这并不真正适用于您的问题,但是为了测试f(x)您知道x必须位于范围内的形式的简单数值函数[x_min, x_max),您通常还会测试f(x_min-1), f(x_min), f(x_max-1), f(x_max). (如果您的内部棋盘表示带有溢出边缘,则它可能与棋盘游戏相关)
  3. 测试已知错误。如果您遇到了您的 .com 无法识别的合法举动,请将其IsSingleMoveValid()添加为测试用例,然后修复您的代码。保留这样的测试用例以防止未来的回归是有用的(一些未来的代码添加/修改可能会重新引入这个错误,并且测试会捕获它)。

测试覆盖率(测试覆盖的代码行的百分比)是一个可以通过gcov等工具计算的目标。您应该自己进行成本效益分析,以测试代码的彻底程度。但是对于像游戏程序中的合法移动检测这样重要的东西,我建议你在这里保持警惕。

其他人已经评论了将测试分解为较小的子测试。其命名法是,这种孤立的功能是用单元测试来测试的,而高级代码中这些功能之间的协作是用集成测试来测试的。

于 2012-05-17T15:20:45.327 回答
2

通常,通过将复杂类分解为多个更简单的类,每个类都执行定义明确且易于测试的任务。

于 2012-05-17T15:14:02.603 回答
2

如果您正在编写测试,那么最简单的做法是将您的IsSingleMoveValid函数分解为更小的函数并单独测试它们。

于 2012-05-17T15:14:48.420 回答
1

正如您在 Wikipedia 上看到的那样,TDD - 测试驱动开发意味着首先编写测试。

在您的情况下,这意味着建立所有有效的移动并为它们编写一个测试函数。然后,您为每个中断测试编写代码,直到所有测试通过。

...不幸的是,有很多案例要测试,即使我测试了几个案例,有些可能会逃脱。

正如其他人所说,当一个函数太复杂时,是时候重构了!

我强烈建议您阅读 Martin Fowler 的《重构 - 改进现有代码的设计》一书,并由 Kent Beck 和其他人贡献。它既是一本学习书,也是一本参考书,这使得它在我看来非常有价值。

This is probably the best book on refactoring and it will teach you how to split your function without breaking everything. Also, refactoring is a really important asset for TDD. :)

于 2012-05-17T15:43:26.737 回答
0

没有“太多的案例无法测试”这样的事情。如果可以编写处理一组案例的代码,则需要考虑。如果它们可以被编写并被思考,那么我们也可以编写测试它们的代码。平均而言,对于您编写的每 10 行(可测试)代码,您可以添加一个与之关联的测试代码的常数因子。

当然,整个技巧是知道如何编写与可测试描述相匹配的代码。

因此,您需要首先为所有案例编写测试。

如果有一个大的,让我们说为了讨论你有一组可数的可能情况来测试(即:对于所有 n 和 m 整数, add(n,m) == n+m ),但你的实际代码真的很简单;返回 n+m。这当然是微不足道的,但不要错过重点:您不需要测试棋盘中所有可能的移动,TDD 旨在使您的测试涵盖所有代码(即:测试执行所有 if 分支您的代码),不一定是所有可能的值或状态组合(呈指数级增长)

一个具有 80-90% 行覆盖率的项目,意味着您的测试在每 10 行代码中执行 9 行。通常,如果您的代码中存在错误,则在大多数情况下会在走特定代码路径时得到证明。

于 2012-05-17T15:23:03.673 回答