我应该为所有东西编写单元测试吗?

时间:2009-02-05 07:56:45

标签: unit-testing tdd mocking

我想知道我应该为所有内容编写单元测试。有些类很难编写单元测试。例如,我正在编写一些处理音频的程序。用于从麦克风捕获音频的类,以及用于播放音频到扬声器的类,如何为这些类编写单元测试?我无法获得这些类的输出和输入,因此几乎不可能测试它们?我唯一能做的测试就是吸气器和定位器,那些无聊的测试。所以问题是,编写单元测试的指导原则是什么?我应该如何处理这些类很难测试?

13 个答案:

答案 0 :(得分:38)

在有意义的地方使用单元测试 - 不要以100%的覆盖率为目标。主要准则是思考而不是应用教条或懒惰。

话虽如此:如果你有自然难以测试的课程,试着减少他们必须做的事情。隔离不可测试的代码并将其API分离到一个接口中。然后针对模拟或存根测试使用该API的逻辑。

答案 1 :(得分:14)

我只写单元测试,我知道它可以节省我的时间。当我开始进行单元测试时,这只是一小部分类(那些可怕的ejb !!)。今天我几乎测试了所有内容,并节省了我做的每一件事的总开发时间。如果有一种通过麦克风测试用户输入的有效方法,我也会这样做。但据我所知,以某种方式节省时间是不可能的。

所以我认为你应该对当前“测试能力”中的所有内容进行单元测试。你应该尝试扩展这种能力,但过度扩展确实会发出错误优先级的信号;很有可能还有其他一些值得你注意的测试。 (从技术上讲,我是测试感染但不是 TDD感染

收益递减法则适用于单位测试,与任何其他测试一样多;你在最后5%的回报率非常低而且成本很高。

答案 2 :(得分:4)

我用来决定是否开发单元测试的一条规则:如果你的课程(或任何单元)将被“释放”,那么你一定要考虑编写单元测试。

通过“在野外”,我的意思是:一旦你的代码处于无法预测或控制事物如何与之交互的情况下。因此,通过API公开的类或暴露给用户输入的类应该进行单元测试。

就我个人而言,我认为编写所有的单元测试可能会浪费你的时间。这真的是一个复杂的问题,你需要考虑:

  • 班级有多复杂?死简单的课程可能不值得测试。
  • 班级有多重要?在ATM上运行的代码有望进行单元测试。
  • 您对该课程的使用方式有多少控制权?

然后总是:

  • 项目截止日期是什么时候?
  • 你有多少人力来创造测试?
  • 您的要求是否具体而详细,或者课程是否经常发生变化?

对于困难的课程,也许可以阅读fuzz testing

答案 3 :(得分:2)

简短的回答是,不,但是当你问“我应该为所有事情做X吗?”时,编程中的所有内容都适用于此。

答案越长,你至少应该考虑它,你正在做的事情。为什么你不能把输入模拟成录音课?为什么不让该类从麦克风旁路录音以从文件加载音频并将输出转到文件而不是扬声器。测试可以将输出结果与已知的正确结果进行比较。

有关哲学方面的更多信息,请参阅this

答案 4 :(得分:1)

要回答您的具体问题,请使用环回电缆。将扬声器连接到麦克风输入。编写一个测试,向扬声器呈现并从麦克风中捕获,并验证您播放的内容是否已被捕获。我的建议是使用一个简单的正弦音调,以便FFT可以告诉你是否捕获了相同的东西。

更普遍的问题的答案是肯定的,你应该对所有你能做的事情进行单元测试。这样做可以为以后创造遗产,因此可以放心地进行更改。它确保您的代码按预期工作。它还记录了接口的预期用途。最后,它强制更好的编码风格。通常难以进行单元测试的设计也很差。编写可测试性意味着编写更好的设计。

答案 5 :(得分:1)

其他示例:如果您开发游戏引擎,则需要测试阴影和其他功能,但必须在视觉上进行确认 - 这不是经典的UnitTest可以决定的。

你的案例:随着你的应用程序变得越来越复杂,点击你的用户界面来测试你所有的功能总是很痛苦。

我会写一个“交互式TestSuite”,其中显示各种虚拟对话框(为每个测试用例定制),只显示您需要测试的功能。关闭对话框后,系统将提示您是否有预期的行为。但我不确定是否有可用的解决方案可以提供帮助。

答案 6 :(得分:1)

您可能需要查看我的博客中的“测试不可能”系列,了解如何测试的一些想法。

在您的情况下,我建议按照idea of Steve Rowe编写测试,设置扬声器和麦克风,并使用环回电缆测试硬件设置代码以及允许通过扬声器发出数据的API并从麦克风中读取数据。

这是单元测试,但不是自动测试。将其移动到独立的测试套件中,该测试套件不与其他自动测试一起运行。如果要自动化它,请设置具有正确配置的第二台PC(加上环回电缆)并远程运行测试。

之后,您确定硬件设置有效,您可以发送并且您可以接收音频。这允许您独立于硬件测试数据处理类。使用模型来模拟扬声器和麦克风。

答案 7 :(得分:1)

捕获和重放音频的代码无法进行单元测试(尽管您可以测试捕获方法在类未成功绑定到资源时调用时返回错误)。

但是,除非您只是将捕获的声音写入磁盘,否则调用捕获和播放类的代码当然可以。

两条建议:

  • 不要测试编译器(例如getter和setter)
  • 测试可能破坏的所有内容

答案 8 :(得分:1)

便宜的回答:Test everything that could possibly break

最终,你需要了解你所编写的测试的商业价值 - 与你花费的任何其他工作没有什么不同,你自己承诺维护的任何其他代码库。

答案 9 :(得分:1)

我测试了大多数东西。

每当我编写测试时,我也会将测试视为文档或有关如何使用我的代码的说明,以供我和其他人在将来阅读。

我不测试实现。我希望能够在不改变测试的情况下更改实现。

我使用TDD可能需要一两年,所以也许我会成熟并停止。不过到目前为止,我还在学习并认为我没有写足够的测试。

答案 10 :(得分:0)

如果您在设置特定的代码区域以进行测试时遇到困难,则可能需要调查像jMockEasyMock这样的模拟框架。

答案 11 :(得分:0)

我倾向于尽可能多地编写测试。不仅要证明某些东西是有效的,而且要让其他人在以后不可避免地破坏它时显而易见。

我有一个长达1行的方法。在我的应用程序的类似地方,我已经编写了单元测试,但我很匆忙,我认为它不可能失败。我错了,它不起作用: - )

编写单元测试的另一个好处不仅在于测试代码,而且以前从未见过代码的人可以阅读测试并理解代码在特定情况下的工作方式......就像规范一样。

答案 12 :(得分:0)

设计测试是一项后天的技能 - 你得到的测试越多,你就越好。有些事情看起来很难测试,但如果你想几分钟就可以找到一种方法。

测试可能很麻烦 - 例如一个java程序可以启动一个解释器甚至是一个shell,比如bash,它会启动一系列unix过滤器,你希望它们输出相同的二进制文件。不过不用担心 - 测试代码的质量不必与成品中的代码一样高。