当要测试的类很复杂时,如何编写单元测试

How do I write a unit test when the class to test is complicated?

本文关键字:何编写 单元测试 很复杂 测试      更新时间:2023-10-16

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

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

要测试的第一类是

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

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

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

一些准则:

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

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

其他人已经评论了将测试分解为较小的子测试。其命名法是,此类隔离函数通过单元测试进行测试,而高级代码中此类函数之间的协作则通过集成测试进行测试。

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

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

正如你在维基百科上看到的,TDD - Test Driven Development意味着首先编写测试。

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

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

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

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

这可能是关于重构的最好的书,它将教你如何在不破坏所有内容的情况下拆分你的函数。此外,重构是TDD的一项非常重要的资产。:)

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

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

因此,您需要首先为所有情况编写一个测试。

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

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