# 不做单元测试的6大借口


看到了一篇不错的关于单元测试的文章，于是就机翻加改写了一下。作者的观点是适当的，不过稍微欠缺了些数据。原文地址：[https://betterprogramming.pub/why-dont-we-do-unit-testing-e0bb55a38aa2](https://betterprogramming.pub/why-dont-we-do-unit-testing-e0bb55a38aa2)

我开始打算写一篇关于单元测试及其背后的哲学和过程的文章。
我想谈谈完成对项目的一组更改并能够部署它们所带来的满足感，因为数百个单元测试正在通过，并且在生产中出现问题的可能性很小。
然后我意识到我参与过的大多数项目（在计算行业的长期职业生涯中）都没有使用任何类型的单元测试。 因此，我认为检查未使用单元测试的各种原因以及对这些项目的影响可能会有所帮助。

### 没有测试工具

当我在 80 年代开始以编程为生时，这是一个很好的借口。在 pascal 中编写航空电子代码，我们确实编写了测试，但几乎不得不推出我们自己的测试框架。这里没有 fakes、mocks 或 DI。
但是，如果您今天使用 Visual Studio 在 .Net/C# 中编写代码，则没有理由不测试任何重要代码。
VS Code 有一个内置的测试运行器，其他测试运行器也可用。fake 和 mock 框架被广泛使用，另外还有非常优秀的 CI/CD 支持，可以让你在构建过程中自动化的运行单元测试。

> 后面作者又列举了一些例子来证明一个观点：就是测试工具实际上是持续进化的，而且现在已经很强大了，因此他的核心观点应该是在如今这个年代，单元测试工具其实不应该是匮乏的，大家应该有不少的选择。

### 我们没有时间做测试

客户希望我们现在发货。写一些代码就行了。先搞出来吧，没有时间学习有关单元测试的象牙塔软件工程学士学位！
你多久听说过一次？
这种方法通常似乎在最初几周有效。然后，当您陷入莫名其妙的错误和副作用的泥潭时，生产力就会下降。加倍努力，开夜车，让更多的人进行手动测试！
不必如此。我会说你没有时间不做测试。

![Untitled](%E4%B8%8D%E5%81%9A%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E7%9A%846%E5%A4%A7%E5%80%9F%E5%8F%A3%200a0c298766af4bcc8f2c295a00c787df/Untitled.png)

看看上面的图表。橙色线显示“没有做单元测试”项目。
起初，我们可以通过不进行任何测试来节省时间。
随着越来越多的功能被实现，（手动）回归测试的负担呈指数级增长。添加的每个新功能都会带来额外的回归测试负担。
很快，您的项目将不得不在质量、成本和实现每个新功能所需的时间之间做出妥协。
所以是的，最初编写一个好的单元测试可能比手工测试你的功能要长两到三倍。但是经过两三个循环的回归测试，你就会领先——而且只会变得更好。

### 我们发现写单元测试好难

编写测试很难，这是绝对正确的。 编写好的测试更难。但不写测试会让你的生活更加艰难（或者你真的喜欢手动测试你的前任 5 年前写的代码？？？）
如果您的开发人员不知道如何编写好的测试，那么他们可能还不是好的开发人员（目前）。 但是他们可以学习。
写测试用例是一个不断进度熟能生巧的过程， 你做的越多，它就会变得越快越容易。
让最好的开发人员编写“模范”测试。 这些可以被经验不足的开发人员用作模板来创建他们自己的测试。
创建测试基类。 我经常这样做是为了封装经常重用的功能并简化单个测试用例。

### 单元测试是白费力气！

当我花时间编写单元测试时，我从不认为这是浪费时间。
如果我编写代码并手动测试它，我认为这是浪费精力。
在一系列手动测试结束时我会得到什么？ 代码在我做手动测试时候是没问题可以正常工作的，但如果我更改代码，我将不得不重复这些测试（或者更有可能不打扰......）
如果我创建了自动化测试，那么我就有了一个测试套件，我可以随时重新运行这些测试，以验证代码是否仍然按照我编写时预期的方式工作。
更重要的是，如果我离开并且一位同事接管了这段代码，他们就会继承这个祖传的测试套件，他们可以使用这些测试来验证他们的更改没有破坏它，并且代码仍然以我预期的方式工作。
手动测试是浪费精力——手动测试的剩余价值为零（并且通常没有记录，因此不可重复）。 自动化测试可以在未来几年内使用，以证明您的软件仍然按预期工作。

### 但是我们浪费了太多时间来修复失败的测试！

我有没有提到编写单元测试很难？
单元测试的目的是测试程序的核心功能。如果你不确定你的程序做什么（这比你想象的更常见），那么你将无法编写健壮的测试。
如果测试失败，这意味着您的软件不再按照您最初的预期运行。这可能是好事——因为你可能改变了需求（但是你不应该先改变测试来反映新的预期行为吗？）。
这也可能意味着您破坏了代码，至少您现在知道有一个问题需要解决。
编写健壮测试的第一步是准确了解您的软件应该做什么，这不会是坏事吧？
测试驱动开发(TDD) 意味着您的代码在设计时至少需要考虑如何测试它。我建议您考虑 SOLID 原则和 DRY，以及它们如何应用于您正在编写的代码。这将始终使您的代码更好，并且更容易有效地进行测试。
我的经验是，这在各个方面都可以带来更好的代码。

### 但是我们的代码从来没有被设计为进行单元测试！

最困难的事情之一是尝试为现有代码编写测试。 它不是为测试而设计的，您经常会因为尝试测试没有规范的代码而让自己分心——这些代码试图执行许多不同且互不相关的操作（还记得 SOLID 中的“S”吗？）。
也许代码一开始就没有写好？
理想情况下，您在编写代码时编写单元测试。 尝试返回并为现有代码编写测试通常会突出一个事实，即现有代码设计不佳，并且其需求缺乏清晰度。
这通常导致需要修复和更新现有代码，不过这也许不失为一件好事吧，见仁见智了。

### 写在最后

### 多少单元测试就足够了？

作为一个粗略的经验法则，我会说你应该编写至少与你正在测试的代码一样多的单元测试代码。 通常情况下测试代码要多得多。
编写单元测试仍然比不编写测试用例，然后花时间尝试在最终的产品上调试错误要快。

### 这一切有什么好处？

单元测试的最终结果是一组可以在构建 pipeline 中运行的测试，这些测试将阻止任何破坏性修改合并到您的代码库中，或部署到任何生产环境中去。
从长远来看，单元测试将为您节省时间，并使您能够以更少的资源更快地交付质量更好的产品。

