33

在项目的哪些部分编写单元测试几乎或实际上是不可能的?数据访问?FTP?

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

4

21 回答 21

37

在这里,我发现(通过hacked Michael Feathers 所说的可以作为答案的内容:

他说,

如果满足以下条件,则测试不是单元测试:

  • 它与数据库对话
  • 它通过网络进行通信
  • 它涉及文件系统
  • 它不能与您的任何其他单元测试同时运行
  • 你必须对你的环境做一些特殊的事情(比如编辑配置文件)来运行它。

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

通常,单元测试应该很小,它们测试一个方法或几个方法的交互。当您将数据库、套接字或文件系统访问拉入您的单元测试时,它们不再是真正关于这些方法的;它们是关于您的代码与其他软件的集成。

于 2008-09-21T06:36:10.377 回答
13

100% 的覆盖率是一个神话,这并不意味着 80% 的覆盖率是无用的。当然,目标是 100%,在单元测试和集成测试之间,你可以接近它。

在单元测试中不可能预测您的客户会对产品做的所有完全奇怪的事情。一旦您开始发现代码的这些令人难以置信的变态,请确保将针对它们的测试回滚到测试套件中。

于 2008-09-20T21:24:17.300 回答
10

实现 100% 的代码覆盖率几乎总是浪费。这方面有很多资源。

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

于 2008-09-20T21:25:16.690 回答
7

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

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

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

如果测试捕获的问题是“昂贵的”,那么您可以付出努力找出如何测试它并维护该测试。如果测试捕获的问题是微不足道的,那么编写(和维护!)测试(即使存在代码更改)最好是微不足道的。

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

于 2008-09-21T05:05:29.737 回答
4

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

当涉及到代码覆盖率时,您希望 100% 覆盖您实际编写的代码——也就是说,您无需测试第三方库代码或操作系统代码,因为这些代码已经交付给您测试过。除非不是。在这种情况下,您可能需要对其进行测试。或者,如果存在已知的错误,在这种情况下,您可能想要测试错误是否存在,以便您收到有关何时修复错误的通知。

于 2008-09-20T21:44:16.207 回答
3

数据访问是可能的,因为您可以设置一个测试数据库。

通常,“不可测试”的东西是 FTP、电子邮件等。但是,它们通常是您可以依赖的框架类,因此如果您将它们隐藏在抽象后面,则无需进行测试。

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

于 2008-09-20T21:18:38.953 回答
3

GUI 的单元测试也很困难,尽管我猜这并非不可能。

于 2008-09-21T00:08:59.777 回答
2

@GarryShutler

我实际上使用假的 smtp 服务器 (Wiser) 对电子邮件进行了单元测试。确保您的应用程序代码正确:

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

类似的事情可能会为其他服务器做。否则你应该能够模拟 API ......

顺便说一句:100% 的覆盖率只是一个开始……只是意味着所有代码实际上都执行了一次bean ……没有关于边缘情况等。

于 2008-09-20T21:27:49.273 回答
2

大多数需要大量且昂贵(资源或计算时间成本)设置的测试是集成测试。单元测试应该(理论上)只测试代码的小单元。个别功能。

例如,如果您正在测试电子邮件功能,那么创建一个模拟邮件程序是有意义的。该模拟的目的是确保您的代码正确调用邮件程序。查看您的应用程序是否真的发送邮件是一个集成测试。

区分单元测试和集成测试非常有用。单元测试应该运行得非常快。在签入代码之前,应该可以轻松运行所有单元测试。

但是,如果您的测试套件包含许多集成测试(设置和拆除数据库等),您的测试运行很容易超过半小时。在这种情况下,开发人员很可能不会在签入之前运行所有单元测试。

所以回答你的问题:做网络单元测试的东西,更好地实现为集成测试(也不要测试getter/setter - 这是浪费时间;-))。

于 2008-09-20T21:49:43.870 回答
2

在单元测试中,你不应该测试任何不属于你的单元的东西;在他们的上下文中测试单元是另一回事。这就是简单的答案。

我使用的基本规则是,你应该对任何触及你的单元边界的东西(通常是班级,或者你的单元可能是什么)进行单元测试,并模拟其余的。无需测试某些数据库查询返回的结果,只需测试您的单元是否发出正确的查询即可。

这并不意味着你不应该忽略那些难以测试的东西。甚至可以使用正确的工具很好地测试异常处理和并发问题。

于 2008-09-21T00:10:36.870 回答
2

“在单元测试方面有什么不能测试的?” * 只有 getter 和 setter 的 Bean。推理:通常是浪费时间,最好花在测试其他东西上。

于 2008-09-21T03:43:11.650 回答
2

Anything that is not completely deterministic is a no-no for unit testing. You want your unit tests to ALWAYS pass or fail with the same initial conditions - if weirdness like threading, or random data generation, or time/dates, or external services can affect this, then you shouldn't be covering it in your unit tests. Time/dates are a particularly nasty case. You can usually architect code to have a date to work with be injected (by code and tests) rather than relying on functionality at the current date and time.

That said though, unit tests shouldn't be the only level of testing in your application. Achieving 100% unit test coverage is often a waste of time, and quickly meets diminishing returns.

更好的是拥有一组更高级别的功能测试,甚至是集成测试,以确保系统“一旦全部连接起来”就可以正常工作- 根据定义,单元测试不会测试。

于 2008-10-29T10:48:51.887 回答
1

任何需要非常大而复杂的设置的东西。当然你可以测试 ftp(客户端),但是你需要设置一个 ftp 服务器。对于单元测试,您需要一个可重现的测试设置。如果你不能提供它,你就不能测试它。

于 2008-09-20T21:24:36.287 回答
1

您可以测试它们,但它们不会是单元测试。单元测试是不跨越边界的东西,例如跨越线路、访问数据库、运行/与第三方交互、接触未经测试/遗留代码库等。

除此之外的任何事情都是集成测试。

标题中问题的明显答案是你不应该对你的 API 内部进行单元测试,你不应该依赖别人的行为,你不应该测试任何你不负责的东西。

其余的应该足以让您能够在其中编写代码,不多也不少。

于 2008-09-20T22:04:23.507 回答
1

在处理大型项目时,肯定 100% 的覆盖率是一个很好的目标,但对于大多数项目而言,在部署之前修复一两个错误并不一定值得花时间创建详尽的单元测试。

在非常详细的级别上对表单提交、数据库访问、FTP 访问等进行详尽的测试通常只是浪费时间;除非正在编写的软件需要非常高水平的可靠性(99.999% 的东西),否则过多的单元测试可能是过度杀伤和实时接收器。

于 2008-09-20T22:31:22.727 回答
1

我不同意 quamrana 关于不测试第三方代码的回应。这是单元测试的理想用途。如果在新版本的库中引入了错误怎么办?理想情况下,当发布新版本的第三方库时,您运行代表该库预期行为的单元测试,以验证它是否仍按预期工作。

于 2008-09-21T06:50:04.757 回答
1

配置是另一个在单元测试中很难很好测试的项目。集成测试和其他测试应该针对配置进行。这减少了测试的冗余并节省了大量时间。尝试对配置进行单元测试通常是轻率的。

于 2008-10-20T19:18:45.627 回答
1

FTP、SMTP、I/O 通常应使用接口进行测试。该接口应该由一个适配器(用于真实代码)和一个用于单元测试的模拟来实现。

没有单元测试应该使用真正的外部资源(FTP 服务器等)

于 2010-11-07T23:02:26.910 回答
0

如果用于设置单元测试所需状态的代码变得比要测试的代码复杂得多,我倾向于划清界限,并找到另一种方法来测试功能。那时你必须问你怎么知道单元测试是正确的!

于 2008-09-20T21:24:05.010 回答
0

FTP、电子邮件等您可以使用服务器仿真进行测试。这很困难,但可能。

不可测试的是一些错误处理。在每个代码中都有永远不会发生的错误处理。例如,在 Java 中必须捕获许多异常,因为它是接口的一部分。但是使用过的实例永远不会抛出它。或者,如果所有可能的情况下都存在 case 块,则使用 switch 的默认情况。

当然,可以删除一些不需要的错误处理。但是将来是否存在编码错误,那么这很糟糕。

于 2008-09-20T21:29:00.893 回答
0

首先对代码进行单元测试的主要原因是验证代码的设计。获得 100% 的代码覆盖率是可能的,但不能不使用模拟对象或某种形式的隔离或依赖注入。

请记住,单元测试不是针对用户的,它们是针对开发人员和构建系统的,用于在发布之前验证系统。为此,单元测试应该运行得非常快,并且尽可能少的配置和依赖摩擦。尽量在内存中做尽可能多的事情,并避免使用测试中的网络连接。

于 2008-09-20T21:51:37.003 回答