什么是单元测试?

229

我看到很多问题问如何在特定语言中进行单元测试,但没有问题问“什么”,“为什么”和“何时”。

  • 它是什么?
  • 它对我有什么作用?
  • 为什么我应该使用它?
  • 什么时候应该使用它(也包括什么时候不需要)?
  • 有哪些常见的陷阱和误解?

Uber,有很多可用的资源,维基百科文章肯定是一个不错的起点。一旦你有了基本的想法,也许一些更具体的问题能帮助你决定是否要使用单元测试。 - palehorse
整洁代码的演讲: http://www.youtube.com/watch?v=wEhu57pih5w - Kieran
1
那不是一个具体的问题,而是五个广泛的问题。 - Raedwald
12
很好你问了这个问题,相比于阅读在线资源,阅读一位工作程序员的答案更加精准和直接。 - Pratyush Dhanuka
20个回答

218

单元测试大致上是使用测试代码对你的代码片段进行隔离测试。它所带来的直接好处包括:

  • 运行测试可以自动化和可重复
  • 你可以在比通过图形用户界面进行点选和点击测试更精细的层次上进行测试

请注意,如果你的测试代码写入文件、打开数据库连接或执行网络操作,则更适合将其归类为集成测试。集成测试也很重要,但不应将其与单元测试混淆。单元测试代码应该简短、简洁且执行速度快。

另一种理解单元测试的方法是,先编写测试。这被称为测试驱动开发(TDD)。TDD 带来了额外的优势:

  • 你不会编写“我可能未来需要这个”的投机代码——只编写足够使测试通过的代码
  • 你编写的代码总是有测试覆盖的
  • 通过首先编写测试,你被迫思考如何调用代码,这通常会从长远来看改进代码的设计。

如果你现在还没有进行单元测试,我建议你开始尝试。找一本好书,任何一本 xUnit 书籍都可以,因为它们之间的概念非常可转移。

有时编写单元测试会很痛苦。当这种情况发生时,尝试寻找帮助,并抵制“只编写该死的代码”的诱惑。单元测试很像洗碗。它并不总是愉快的,但它可以保持你比喻中的厨房清洁,而你真的希望它是干净的。 :)


编辑:我想到了一个误解,尽管我不确定它是否很普遍。我听过一个项目经理说,单元测试让团队把所有的代码都重写了一遍。如果看起来和感觉不对,那么你做错了。编写测试通常不仅可以加快开发速度,而且还可以为你提供方便的“现在我完成了”指示器,否则你将无法获得这个指示器。


4
您能详细说明一下TDD如何加速开发的观点吗? - xyhhx
测试驱动开发只是为了确保编写了单元测试而发明的 :) - yılmaz

72

我不反对Dan的说法(虽然更好的选择可能是不回答)......但是......

单元测试是编写代码来测试系统行为和功能的过程。

显然,测试可以提高您的代码质量,但这只是单元测试的表面好处。真正的好处在于:

  1. 使技术实现变化更容易,同时确保不改变行为(重构)。经过适当单元测试的代码可进行积极的重构/清理,几乎没有破坏任何东西而不知道的可能性。
  2. 开发人员在添加行为或进行修复时具备自信心。
  3. 记录您的代码
  4. 指示紧密耦合的代码区域。紧密耦合的代码难以进行单元测试
  5. 提供使用API并尽早查找困难的手段
  6. 指示不太一致的方法和类

您应该进行单元测试,因为提供维护性和质量产品给客户符合您的利益。

我建议在任何模拟现实世界行为的系统或系统的一部分中使用它。换句话说,它特别适用于企业开发。我不会将其用于一次性/实用程序。我也不会将其用于难以测试的系统部分(UI是常见的例子,但并非总是如此)

最大的陷阱是开发人员测试的单元过大,或将方法视为单元。如果您不理解控制反转,这一点尤其正确 – 在这种情况下,您的单元测试将始终变成端到端的集成测试。单元测试应该测试个别行为 - 大多数方法具有许多行为。

最大的误解是程序员不应该进行测试。只有糟糕或懒惰的程序员才持这种观点。修建你屋顶的工人不应该测试吗?更换心脏瓣膜的医生不应该测试新瓣膜吗?只有程序员能够测试他的代码是否按照他的意图执行(QA可以测试边缘情况-当代码被告知执行程序员不想要的操作时,代码如何行为,客户可以进行验收测试-代码是否完成了客户支付的任务)。


46

单元测试与“只需打开一个新项目并测试特定代码”相比的主要区别在于它是自动化的,因此可重复。

如果您手动测试代码,它可能会让您相信代码在其当前状态下完美地工作。但是,当您一周后对其进行了微小修改时呢?您愿意每次代码中的任何更改时都手动重新测试吗?很可能不会 :-(

但是,如果您可以随时以几秒钟的时间内单击运行测试,并且以完全相同的方式执行测试,那么它们将立即向您显示什么已经出现问题。如果您还将单元测试集成到自动构建过程中,则它们甚至会在看似完全无关的更改导致代码库中某个遥远部分出现问题时警告您-这时,您甚至不会想到需要重新测试该特定功能。

这是单元测试胜过手动测试的主要优势。 但是等等,还有更多:

  • 单元测试极大地缩短了开发反馈循环:使用单独的测试部门,您可能需要数周时间才能知道代码中存在错误,而此时您已经忘记了许多上下文,因此可能需要花费几个小时来查找和修复错误;然而,对于单元测试,反馈周期以秒计算,并且错误修复过程通常是“哦,该在这里检查那个条件的”:-)
  • 单元测试有效地记录了您对代码行为的理解
  • 单元测试迫使您重新评估设计选择,从而产生更简单,更清晰的设计

相应的,单元测试框架使您可以轻松编写和运行测试。


此外,我最喜欢测试代码的部分(特别是在给定新代码库时):它展示了被测试代码的预期使用方式。 - Steven Evers

38

我在大学里从未学过单元测试,花了一段时间才“懂”它。我读了一些介绍,想到了“啊,对了,自动化测试,可能很棒”,然后就把它忘了。

在我真正理解单元测试的意义之前,又过了相当长的一段时间: 假设你正在开发一个大型系统并编写一个小模块。它编译通过了,你测试了一下,结果很好,然后你就转向下一个任务。九个月后,两个版本后,有人对程序中某个看似无关的部分进行了更改,但这破坏了该模块。更糟的是,他们测试了自己的更改,他们的代码工作正常,但他们没有测试你的模块; 甚至他们可能不知道你的模块存在

现在你有一个问题:有错误的代码在主干分支上,而且没有人知道。最好的情况是内部测试人员在你发货之前找到它,但在游戏的最后时刻修复代码是昂贵的。如果没有内部测试人员发现它......嗯,那可能会变得非常昂贵。

解决方案就是单元测试。它们将在编写代码时捕获问题——这很好——但你可以手动完成。真正的回报是,当你现在正在处理完全不同的项目时,一名暑期实习生认为如果那些参数按字母顺序排列会更整洁——然后你之前编写的单元测试失败了,有人用东西扔向实习生,直到他把参数顺序改回去。就是单元测试的“为什么”。 :-)


14

在探索TDD的道路上,对于单元测试和TDD的哲学优点,以下是我初步的、关键"启发式"观察(并不原创或一定是新闻)...

  1. TDD不意味着编写两倍的代码。测试代码通常编写起来相当快速和轻松,并且是设计过程的关键部分和至关重要。

  2. TDD帮助您意识到何时停止编码!您的测试让您有信心,认为您已经足够了,可以停止调整并继续进行下一步操作。

  3. 测试和代码共同努力实现更好的代码。您的代码可能很糟糕/有错误。您的测试也可能很糟糕/有错误。在TDD中,您指望两者都很糟糕/有错误的可能性相当低。通常是测试需要修复,但这仍然是一个好结果。

  4. TDD有助于解决编码便秘的问题。您知道有那种感觉,有很多事情要做,你几乎不知道从哪里开始?现在是周五下午,如果你再拖延几个小时...TDD允许您快速地实现您认为需要做的事情,并快速启动您的编码。而且,就像实验室老鼠一样,我认为我们都会响应那个大绿灯,并努力再次看到它!

  5. 同样地,这些设计师类型可以看到他们正在处理的内容。他们可以走开喝果汁/抽烟/玩iphone,然后回到一个立即给他们一个视觉提示的监视器。TDD给了我们类似的东西。当生活干预时,更容易看出我们到了哪里...

  6. 我想是福勒说过:"不完美的测试,经常运行,要比完美的测试根本没有写好得多"。我理解这意味着允许我在我认为它们最有用的地方编写测试,即使我的代码覆盖率非常不完整。

  • TDD在各种意想不到的方面有所帮助。良好的单元测试可以帮助记录某个功能应该如何运行,它们可以帮助您将代码从一个项目迁移到另一个项目,并让您对未进行测试的同事产生了一种无端的优越感 :)

  • 这个演示文稿是介绍所有与测试相关内容的最佳入门材料。


    所有这些答案都真的鼓励我开始学习和使用单元测试,即使可能需要很多时间。但是,伙计,你的链接无法使用,如果你能修复它,那就太棒了) - Isi

    7
    我推荐您阅读Gerard Meszaros的xUnit测试模式书籍。这本书内容丰富,是关于单元测试的极好资源。以下是他的网站链接,其中介绍了单元测试的基础知识。http://xunitpatterns.com/XUnitBasics.html

    5

    我使用单元测试来节省时间。

    在构建业务逻辑(或数据访问)时,测试功能通常涉及将内容输入到许多可能尚未完成的屏幕中。自动化这些测试可以节省时间。

    对我而言,单元测试是一种模块化测试工具。通常每个公共函数都至少有一个测试。我编写其他测试以覆盖各种行为。

    您在开发代码时想到的所有特殊情况都可以记录在单元测试的代码中。单元测试也成为使用代码的示例来源。

    对于我来说,通过单元测试发现我的新代码破坏了什么比提交代码并由前端开发人员发现问题要快得多。

    对于数据访问测试,我尝试编写没有更改或清理自己的测试。

    单元测试无法解决所有测试需求。它们将能够节省开发时间并测试应用程序的核心部分。


    4
    LibrarIES(如NUnitxUnitJUnit)只是必须的,如果您想使用Kent Beck所推广的TDD方法来开发项目。您可以阅读TDD简介或Kent Beck的书测试驱动开发:实例解析

    如果你想确保你的测试覆盖了代码的“好”部分,你可以使用像NCover, JCover, PartCover等软件。它们会告诉你代码的覆盖率百分比。根据你在TDD方面的熟练程度,你会知道自己是否已经足够好地实践了它 :)


    4
    这是我的看法。我认为单元测试是编写软件测试的一种实践,以验证您的真实软件是否按照预期执行。这始于Java世界中的jUnit,并已成为PHP中的SimpleTestphpUnit的最佳实践。这是极限编程的核心实践,可以确保在编辑后您的软件仍然按照预期工作。如果您有足够的测试覆盖率,就可以进行大规模重构、修复错误或快速添加功能,而不必担心引入其他问题。
    当所有单元测试都可以自动运行时,它最有效。
    单元测试通常与OO开发相关联。基本思想是创建一个脚本,为您的代码设置环境,然后对其进行测试;您编写断言,指定应该接收到的预期输出,然后使用上述框架之一执行测试脚本。
    该框架将针对您的代码运行所有测试,然后报告每个测试的成功或失败。 phpUnit默认情况下从Linux命令行运行,但也有可用于它的HTTP接口。 SimpleTest天生是基于Web的,更容易上手和运行,我认为。结合xDebug使用,phpUnit可以为代码覆盖率提供自动化统计数据,一些人发现这非常有用。
    一些团队从其Subversion存储库编写挂钩,以便在提交更改时自动运行单元测试。
    将单元测试保存在与应用程序相同的存储库中是一个好习惯。

    3

    单元测试是编写测试应用程序代码的代码。

    名称中的“单元”部分指的是一次测试小代码单元(例如一个方法)的意图。

    xUnit可帮助进行此测试-它们是协助进行此操作的框架。其中一部分是自动化测试运行器,告诉您哪些测试失败,哪些测试通过。

    它们还具有在每个测试之前设置需要的公共代码并在所有测试完成后进行拆除的功能。

    您可以进行测试以检查是否已引发了预期的异常,而无需自己编写整个try catch块。


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