关于单元测试,有哪些不需要测试的内容?

34

在项目的哪些部分编写单元测试几乎或真正不可能?数据访问?ftp?

如果这个问题有答案,那么 %100 的覆盖率就是一个神话,不是吗?


为什么这个问题没有被关闭为重复?https://dev59.com/9HVD5IYBdhLWcg3wKoWH,https://dev59.com/vnVD5IYBdhLWcg3wL4YA等。 - raven
3
我认为这三个问题不同,必须在不同的页面上进行讨论。 - spinodal
100%的代码覆盖率不是神话,而是一条渐近线。 - James
21个回答

37

这里我通过haacked的博客发现了Michael Feathers的观点,他相信以下几点可能成为区分单元测试的标准:

他说:

如果一个测试:

  • 与数据库通信
  • 跨网络通信
  • 涉及文件系统
  • 不能同时运行于其他单元测试中
  • 需要对环境进行特殊设置(例如编辑配置文件)才能运行
那么它就不是一个单元测试。

同一篇文章中,他补充道:

通常情况下,单元测试应该很小,测试一个方法或者两个方法之间的交互。当你把数据库、套接字或者文件系统访问引入到你的单元测试中时,它们不再仅仅关注于那些方法本身,而是关于你的代码与其他软件集成的问题。


我完全不同意这个观点。如果依赖于数据库的代码被重构了怎么办? - Sklivvz
2
@Skliwz:这就是接口/抽象类和模拟对象的作用。如果设计得好并且数据库进行了重构,那么受影响的类只需要考虑与数据库的接口即可。 - Spoike

13

认为100%的测试覆盖率是一种神话,但这并不意味着80%的测试覆盖率没有用。当然,目标是达到100%,通过单元测试和集成测试,可以逐步接近这个目标。

在单元测试中,不可能预测顾客对产品做出的所有奇怪操作。一旦开始发现这些令人难以置信的代码变形,确保将它们的测试回归到测试套件中。


奇怪简直是个轻描淡写的词。 - johnny

10

实现100%的代码覆盖几乎总是浪费的。有许多相关资源。

没有什么是不可能进行单元测试的,但总会有收益递减。对于难以进行单元测试的东西进行单元测试可能不值得。


7

目标并不是100%的代码覆盖率,也不是80%的代码覆盖率。编写单元测试容易并不意味着您应该编写它,而编写单元测试困难并不意味着您应该避免努力。

任何测试的目标都是以最实惠的方式检测用户可见问题。

编写、维护和诊断测试所标记的问题(包括误报)的总成本是否值得特定测试捕捉到的问题?

如果测试捕捉到的问题“昂贵”,那么您可以承担努力找出如何测试它并维护该测试的成本。如果测试捕捉到的问题微不足道,则编写(并维护!)该测试(即使在代码更改的情况下)也应该很简单。

单元测试的核心目标是保护开发人员免受实现错误的影响。这就表明过度努力将是一种浪费。在某个点之后,有更好的策略来获得正确的实现。此外,在某个点之后,用户可见的问题是由于正确实现了错误的事物,只能通过用户级别或集成测试来捕获。


4

你不会测试什么?任何可能不会出错的东西。

在代码覆盖率方面,您要目标是实际编写的100%代码 - 即您不需要测试第三方库代码或操作系统代码,因为该代码已经经过测试交付给您。除非它没有。在这种情况下,您可能需要测试它。或者如果已知存在错误,则可能要测试错误的存在,以便在修复错误时获得通知。


你不会测试什么?任何不可能出错的东西。显然,这样的事情是不存在的 ;) - Thomas
我发现那些看似不可能出错的东西,却是最难找出错误的地方。我想知道为什么? - Matthew Scouten
1
稳住!我只是引用传统的敏捷思想:测试所有可能出错的东西。像访问器这样的东西被认为是“不易破坏”的,因此不需要显式测试,它们会在测试其他东西的过程中得到测试。 - quamrana
这句话或许应该重新表述为:“任何不可能在编译单元的解释器被破坏之前就已经崩溃的东西”,或者直接说“任何不可能在运行时被破坏的东西”。 - Steen

3

你可以设置一个测试数据库,从而实现数据访问。

通常,“无法测试”的内容是FTP、电子邮件等。然而,它们通常是框架类,可以依赖它们,因此如果将其隐藏在抽象层后,则不需要进行测试。

另外,仅有100%的代码覆盖率是不够的。


基于测试数据库的测试仍可能因网络问题而失败,除非您所指的是内存数据库。 - James McMahon
很高兴看到有人提倡使用真实数据进行测试。我们通常会模拟/存根一些数据,但会使用我们的数据库确保测试有效。 - Beep beep
但是与测试数据库交互将是集成测试而不是单元测试... 是吗? - Robert Koritnik
从技术上讲,可以。但是你需要使用一个单元测试框架来执行它,所以这取决于他们所说的单元测试的广度。 - Garry Shutler
@Garry:是啊...既然我们可以使用“单元测试框架”来进行单元测试和集成测试,也许现在是时候开始简单地称之为“测试框架”了。 - Alfred Myers
我认为讨论语义是毫无意义的。编写单元测试是为了在保持合同有效的同时允许对代码进行大量重构。在这种情况下,没有任何不应该被覆盖的代码。 - Sklivvz

3

对于GUI界面的单元测试也是很困难的,虽然不是不可能,我想。


有一些自动化测试GUI的工具,虽然我自己没有使用过。 - rlerallut

2

我实际上是使用一个假的SMTP服务器(Wiser)来进行电子邮件单元测试。这样可以确保您的应用程序代码正确:

http://maas-frensch.com/peter/2007/08/29/unittesting-e-mail-sending-using-spring/

其他服务器也可能有类似的做法,否则您应该能够模拟API...

顺便说一下:100%的覆盖率只是个开始...这只意味着所有代码已经被执行了一次....并没有考虑边缘情况等。


是的,这是可能的,但那还算单元测试吗?你如何区分单元测试和集成测试之间的界限? - philant
你是对的;使用虚假服务器将是集成测试...模拟它们的接口将是单元测试。 - p3t0r

2
大多数需要庞大且昂贵(在资源或计算时间成本方面)设置的测试都是集成测试。理论上,单元测试应该只测试代码的小部分,即个别函数。
例如,如果您正在测试电子邮件功能,则创建模拟邮件程序是有意义的。该模拟的目的是确保您的代码正确调用邮件程序。要查看应用程序是否实际发送电子邮件是一项集成测试。
区分单元测试和集成测试非常有用。单元测试应该非常快速运行。在检查代码之前运行所有单元测试应该很容易。
但是,如果您的测试套件由许多集成测试(设置和撤消数据库等)组成,则测试运行很容易超过半小时。在这种情况下,开发人员很可能不会在检入之前运行所有单元测试。
因此,回答您的问题:不要对最好作为集成测试实施的事物进行单元测试(也不要测试getter/setter-这是浪费时间的;-))。

2
在单元测试中,你不应该测试任何与你的单元无关的内容;在上下文中测试单元是另一回事。这就是简单的答案。
我使用的基本规则是:你应该对触及你的单元边界(通常是类或其他单元)的任何内容进行单元测试,并模拟其余部分。没有必要测试某个数据库查询返回的结果,仅需要测试你的单元是否输出了正确的查询。
这并不意味着你不应该省略一些难以测试的内容;即使是异常处理和并发问题也可以使用正确的工具进行很好的测试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接