什么是单元测试?

时间:2008-08-04 16:27:40

标签: unit-testing glossary

我看到许多问题询问'如何'用特定语言进行单元测试,但毫无疑问,问'什么','为什么'和'何时'。

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

20 个答案:

答案 0 :(得分:194)

粗略地说,单元测试是用测试代码单独测试代码的一部分。想到的直接好处是:

  • 运行测试变得可自动化且可重复
  • 您可以通过GUI
  • 进行比点击式测试更精细的测试

请注意,如果您的测试代码写入文件,打开数据库连接或通过网络执行某些操作,则更适合将其归类为集成测试。集成测试是一件好事,但不应与单元测试混淆。单元测试代码应简短,美观且快速执行。

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

  • 你不会写出推测性的“我将来可能需要这个”代码 - 足以让测试通过
  • 您编写的代码始终包含在测试中
  • 首先编写测试,你不得不考虑如何调用代码,这通常会改善代码的设计。

如果您现在没有进行单元测试,我建议您开始使用它。得到一本好书,几乎任何xUnit-book都会做,因为这些概念在它们之间是可以转移的。

有时候编写单元测试会很痛苦。当它成功的时候,试着找人来帮助你,并抵制“只写那该死的代码”的诱惑。单元测试就像洗碗一样。它并不总是令人愉快,但它让你的隐喻厨房变得干净,你真的希望它变得干净。 :)


编辑:我想到了一个误解,虽然我不确定它是否如此常见。我听说项目经理说单元测试让团队编写了两次所有代码。如果它看起来和感觉那样,那么,你做错了。编写测试通常不仅可以加快开发速度,而且还可以为您提供一个方便的“现在我已经完成”的指标,否则您将不会这样做。

答案 1 :(得分:69)

我不同意丹(虽然更好的选择可能只是不回答)......但是......

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

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

  1. 在确保不改变行为(重构)的同时,更轻松地更改技术实现。正确的单元测试代码可以被积极地重构/清理,几乎没有机会破坏任何东西而不会注意到它。
  2. 在添加行为或修复时为开发人员提供信心。
  3. 记录您的代码
  4. 指明代码紧密耦合的区域。单元测试紧密耦合的代码很难
  5. 提供使用API​​的方法,并尽早寻找困难
  6. 表示不具有凝聚力的方法和类
  7. 您应该进行单元测试,因为您有兴趣为您的客户提供可维护和优质的产品。

    我建议你将它用于模拟现实行为的任何系统或系统的一部分。换句话说,它特别适合企业发展。我不会将它用于丢弃/实用程序。我不会将它用于测试有问题的系统部分(UI是一个常见的例子,但情况并非总是如此)

    最大的缺陷是开发人员测试的单位太大,或者他们认为某个方法是一个单位。如果您不理解Inversion of Control,则尤其如此 - 在这种情况下,您的单元测试将始终转变为端到端集成测试。单元测试应测试个人行为 - 大多数方法都有很多行为。

    最大的误解是程序员不应该测试。只有糟糕或懒惰的程序员才会相信。建造屋顶的人不应该测试吗?更换心脏瓣膜的医生是否应该测试新的瓣膜?只有程序员才能测试他的代码是否按照他的意图进行测试(QA可以测试边缘情况 - 代码在被告知做程序员不想要的事情时的行为,客户端可以进行验收测试 - 代码是否执行客户支付的费用是什么)

答案 2 :(得分:44)

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

如果您手动测试代码,可能会让您确信代码运行正常 - 处于当前状态。但是一周后,当你对它做了一点修改时呢?当代码中的任何更改时,您是否愿意再次手动重新测试?最有可能的不是: - (

但是如果你可以随时运行你的测试,只需一次点击,完全相同的方式,在几秒钟内,那么他们 会立即显示你的东西破碎。如果你还将单元测试集成到你的自动构建过程中,他们会提醒你注意错误,即使在一个看似完全不相关的变化破坏了代码库的一个遥远的部分 - 甚至在你甚至不会发生的情况下需要重新测试该特定功能。

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

  • 单元测试显着缩短开发反馈循环:对于单独的测试部门,您可能需要数周时间才能知道代码中存在错误,此时您已经忘记了很多错误上下文,因此可能需要花费数小时才能找到并修复错误; OTOH进行单元测试,反馈周期以秒为单位进行测量,错误修复过程通常沿着“哦sh * t,我忘了检查这个条件”: - )
  • 单元测试文档(您对代码行为的理解)
  • 单元测试迫使您重新评估您的设计选择,从而导致更简单,更清洁的设计

反过来,单元测试框架使您可以轻松编写和运行测试。

答案 3 :(得分:33)

我从未在大学教过单元测试,我花了一段时间才“搞定”它。我读到了它,“啊,对,自动测试,我觉得这很酷”,然后我就忘记了。

在我真正弄清楚这一点之前花了相当长的时间:让我们假设你正在研究一个大型系统并编写一个小模块。它编译,你通过它的步伐,它工作得很好,你继续下一个任务。九个月后,两个版本后来其他人改变了一些看似无关的程序部分,并打破了模块。更糟糕的是,他们测试他们的更改,他们的代码是有效的,但他们不测试你的模块;他们甚至不知道你的模块存在

现在你遇到了一个问题:破坏的代码在主干中,甚至没有人知道。最好的情况是内部测试人员在您发货之前找到它,但修复游戏后期的代码是昂贵的。如果没有内部测试人员发现它......那么,确实会非常昂贵。

解决方案是单元测试。当你编写代码时,它们会遇到问题 - 这很好 - 但你可以手工完成。真正的回报是,当你现在正在开展一个完全不同的项目时,他们会在九个月后遇到问题,但是一个暑期实习生认为如果这些参数按字母顺序排列会更整洁 - 然后是单元测试你写回来的方法失败了,有人向实习生抛出东西,直到他改变参数顺序为止。 那是单元测试的“原因”。 : - )

答案 4 :(得分:13)

关于单元测试和TDD的哲学专业知识,这里有一些关键的“灯泡”观察,这些观察让我在TDD启蒙的道路上暂时迈出了初步的步伐(没有原创或必然的消息)...... / p>

  1. TDD并不意味着写入两倍的代码。测试代码通常可以非常快速且轻松地编写,并且是您设计过程的关键部分,并且非常关键。

  2. TDD帮助您了解何时停止编码!你的测试让你相信你已经做了足够的事情,可以停止调整并继续下一步。

  3. 测试和代码协同工作以实现更好的代码。你的代码可能很糟糕/错误。你的测试可能很糟糕/错误。在TDD中,你可以看出两个人很差/错误的可能性很小。通常它的测试需要修复,但仍然是一个很好的结果。

  4. TDD有助于编码便秘。你知道那种感觉你有很多事要做,你几乎不知道从哪里开始?这是星期五下午,如果你拖延几个小时...... TDD可以让你很快充实你认为你需要做的事情,并让你的编码迅速发展。此外,就像实验室老鼠一样,我想我们都会对那个大绿灯做出反应,并且更加努力地再次看到它!

  5. 类似地,这些设计师类型可以看到他们正在做什么。他们可以徘徊在果汁/香烟/ iphone休息时间,并返回显示器,立即给他们一个关于他们到达的地方的视觉提示。 TDD给了我们类似的东西。当生活介入时,更容易看到我们到达的地方......

  6. 我认为Fowler说:“不完美的测试,经常运行,比完全没有写过的完美测试要好得多”。我将此解释为允许我编写测试,即使我的其余代码覆盖率严重不完整,我认为它们也是最有用的。

  7. TDD以各种令人惊讶的方式提供帮助。良好的单元测试可以帮助记录应该做的事情,它们可以帮助您将代码从一个项目迁移到另一个项目,并且让您对非测试同事有一种无根据的优越感:)

  8. This presentation是对所有美味健康测试的绝佳介绍。

答案 5 :(得分:7)

我想推荐Gerard Meszaros的xUnit Testing Patterns一书。它很大但是在单元测试方面是一个很好的资源。这是他的网站的链接,他在那里讨论了单元测试的基础知识。 http://xunitpatterns.com/XUnitBasics.html

答案 6 :(得分:5)

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

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

对我来说,单元测试是一种模块化的测试工具。每个公共职能通常至少有一个测试。我写了额外的测试来涵盖各种行为。

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

我发现我的新代码在我的单元测试中破坏了某些内容然后检查代码并让一些前端开发人员发现问题,这要快得多。

对于数据访问测试,我尝试编写无需更改或自行清理的测试。

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

答案 7 :(得分:4)

如果您想使用Kent Beck推广的NUnit方法开发项目,那么xUnitJUnitTDD这样的图书馆就是强制性的。

您可以阅读 Introduction to Test Driven Development (TDD) 或Kent Beck的书 Test Driven Development: By Example

然后,如果您想确保您的测试涵盖代码的“好”部分,则可以使用NCoverJCoverPartCover等软件。他们会告诉你代码的覆盖率。根据你对TDD的熟练程度,你会知道你是否已经足够好地练习它了。)

答案 8 :(得分:4)

这是我的看法。我会说单元测试是编写软件测试的实践,以验证您的真实软件是否符合它的意图。这开始于Java世界中的jUnit,并且已成为PHP中的最佳实践,SimpleTestphpUnit。它是极限编程的核心实践,可帮助您确保软件在编辑后仍能按预期工作。如果您有足够的测试覆盖率,您可以快速进行重构,修复错误或添加功能,而不必担心引入其他问题。

当所有单元测试都可以自动运行时,它是最有效的。

单元测试通常与OO开发相关。基本思想是创建一个脚本,为您的代码设置环境然后进行练习;你编写断言,指定你应该收到的预期输出,然后使用如上所述的框架执行测试脚本。

框架将针对您的代码运行所有测试,然后报告每个测试的成功或失败。默认情况下,phpUnit是从Linux命令行运行的,尽管有可用的HTTP接口。 SimpleTest本质上是基于Web的,更容易启动和运行,IMO。与xDebug结合使用,phpUnit可以为您提供代码覆盖率的自动统计信息,有些人认为这些统计信息非常有用。

有些团队从他们的subversion存储库编写钩子,以便在您提交更改时自动运行单元测试。

将单元测试保存在与应用程序相同的存储库中是一种很好的做法。

答案 9 :(得分:3)

单元测试是一种实践,用于确保您要实现的功能或模块将按预期(要求)运行,并确保其在边界条件和无效输入等方案中的行为。< / p>

xUnitNUnitmbUnit等是帮助您编写测试的工具。

答案 10 :(得分:3)

我认为您不理解的一点是,像NUnit(等)这样的单元测试框架将帮助您进行自动化中小型测试。通常你可以在GUI中运行测试(例如,NUnit就是这种情况),只需单击一个按钮,然后 - 希望 - 看到进度条保持绿色。如果它变为红色,框架会显示哪个测试失败以及究竟出了什么问题。在正常的单元测试中,您经常使用断言,例如Assert.AreEqual(expectedValue, actualValue, "some description") - 所以,如果这两个值不相等,您会看到一个错误,说“某些描述:expected&lt; expectedValue&gt;但是&lt; actualValue&gt;”。

因此,作为结论,单元测试将使测试更快,对开发人员来说更舒适。您可以在提交新代码之前运行所有单元测试,这样就不会破坏同一项目中其他开发人员的构建过程。

答案 11 :(得分:3)

使用Testivus。所有你需要知道的就是:)

答案 12 :(得分:3)

单元测试是对代码单元(例如单个函数)的测试,而不需要该代码单元所依赖的基础结构。即单独测试。

例如,如果您正在测试的函数连接到数据库并进行更新,则在单元测试中您可能不希望进行该更新。如果它是一个集成测试你会,但在这种情况下它不是。

因此,单元测试将运行您正在测试的“函数”中包含的功能,而不会产生数据库更新的副作用。

假设您的函数从数据库中检索了一些数字,然后执行标准差计算。你想在这里测试什么?是正确计算标准偏差还是从数据库返回数据?

在单元测试中,您只想测试标准偏差是否正确计算。在集成测试中,您希望测试标准差计算和数据库检索。

答案 13 :(得分:3)

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

Unit 部分名称是关于一次测试小单位代码(例如一种方法)的意图。

xUnit可以帮助进行这项测试 - 它们是帮助实现这一目标的框架。其中一部分是自动测试运行器,它告诉您哪些测试失败以及哪些测试失败。

他们还可以在每次测试中设置您需要的公共代码,并在所有测试完成后将其拆除。

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

答案 14 :(得分:2)

首先,无论是关于单元测试还是任何其他类型的自动化测试(集成,负载,UI测试等),与您的建议的主要区别在于它是自动化的,可重复的,并且不需要要消耗的任何人力资源(=没有人必须执行测试,他们通常只需按一下按钮就可以运行。)

答案 15 :(得分:2)

测试驱动开发已经取代了单元测试一词。作为一个旧计时器,我将提到它的更通用的定义。

单元测试还意味着在更大的系统中测试单个组件。这个单独的组件可以是dll,exe,类库等。它甚至可以是多系统应用程序中的单个系统。因此,最终单元测试最终将测试您想要称为单个较大系统的任何内容。

然后,您将通过测试所有组件如何协同工作来升级到集成或系统测试。

答案 16 :(得分:1)

我参加了FoxForward 2007上的单元测试演示,并被告知永远不要对任何与数据有关的东西进行单元测试。毕竟,如果你测试实时数据,结果是不可预测的,如果你不测试实时数据,你实际上并没有测试你编写的代码。不幸的是,这是我最近做的大部分编码。 : - )

最近,当我编写保存和恢复设置的例程时,我确实对TDD进行了拍摄。首先,我验证了我可以创建存储对象。然后,它有我需要调用的方法。然后,我可以称之为。然后,我可以传递它参数。然后,我可以传递它特定的参数。依此类推,直到我最终验证它是否会保存指定的设置,允许我更改它,然后将其恢复为几种不同的语法。

我没有达到目的,因为我需要 - 例行 - 现在 - 该死,但这是一个很好的练习。

答案 17 :(得分:1)

  

如果给你一堆废话,你会怎么做?看起来你已经陷入了永久的清理状态,你知道添加任何新功能或代码会破坏当前的设置,因为当前的软件就像一个纸牌屋?

     

那我们如何进行单元测试呢?

你从小做起。我刚刚进入的项目直到几个月前才进行单元测试。当覆盖率很低时,我们只需选择一个没有覆盖的文件,然后单击“添加测试”。

现在我们已经达到了40%以上,而且我们已经成功地剔除了大多数低调的成果。

(最好的部分是,即使在这种低覆盖率的情况下,我们已经遇到了许多错误的代码实例,测试也抓住了它。这是推动人们添加更多测试的巨大动力。)

答案 18 :(得分:1)

这就解释了为什么你应该进行单元测试。

以下3个视频涵盖了javascript中的单元测试,但一般原则适用于大多数语言。

单元测试:分钟现在将节省数小时 - Eric Mann - https://www.youtube.com/watch?v=_UmmaPe8Bzc

JS单元测试(非常好) - https://www.youtube.com/watch?v=-IYqgx8JxlU

编写可测试的JavaScript - https://www.youtube.com/watch?v=OzjogCFO4Zo

现在我只是在了解这个主题,所以我可能不是100%正确,而且比我在这里描述的更多,但我对单元测试的基本理解是你编写一些测试代码(与主代码分开),使用函数需要的输入(参数)调用主代码中的函数,然后代码检查它是否返回有效的返回值。如果它确实返回了一个有效值,那么你用来运行测试的单元测试框架会显示绿灯(一切都很好)如果值无效你会得到红灯然后你可以立即解决问题在将新代码发布到生产之前,如果没有测试,您实际上可能没有发现错误。

因此,您为当前代码编写测试并创建代码,以便通过测试。几个月后,您或其他人需要修改主代码中的函数,因为之前您已经为该函数编写了测试代码,您现在再次运行并且测试可能会失败,因为编码器在函数中引入了逻辑错误或者完全返回了一些内容不同于该函数应返回的内容。再次没有进行测试,错误可能很难被追踪,因为它可能会影响其他代码,并且会被忽视。

此外,您有一个运行代码并测试它的计算机程序,而不是您在浏览器中逐页手动执行它,这样可以节省时间(javascript的单元测试)。让我们假设您修改了网页上某些脚本使用的功能,它可以很好地工作并且有利于其新的预期目的。但是,为了论证,我们还要说你的代码中还有另一个函数依赖于新修改的函数来使其正常运行。此相关功能现在可能因为您对第一个功能所做的更改而停止工作,但是如果没有由您的计算机自动运行的测试,您将不会注意到该功能存在问题,直到它实际上已经执行了,您必须手动导航到包含执行相关功能的脚本的网页,然后您才会注意到由于您对第一个功能所做的更改而导致的错误

重申一下,在开发应用程序时运行测试会在您编码时遇到这些问题。如果没有测试,您必须手动完成整个应用程序,即使这样,也很难发现错误,天真地将其发送到生产环境中,一段时间后,一位善良的用户会向您发送错误报告(它在测试框架中不会像你的错误信息一样好。)

当你第一次听到这个主题并且你想到自己时,我还没有测试我的代码,这让人很困惑吗?你编写的代码就像它本应该已经工作一样,&#34;为什么我需要另一个框架?&#34; ...是的,你已经在测试你的代码,但计算机更擅长正在做。你只需要为一个函数/代码单元编写足够好的测试一次,其余的由强大的cpu为你处理,而不是你必须手动检查你的所有代码仍在工作时你做了一个更改你的代码。

此外,如果您不愿意,则不必对代码进行单元测试,但随着项目/代码库开始变大,随着引入错误的机会增加,您无需付出代价。

答案 19 :(得分:0)

单元测试和TDD通常使您可以缩短关于所编写软件的反馈周期。您不必在实施的最后阶段进行大量测试,而是逐步测试您编写的所有内容。这会极大地提高代码质量,正如您所看到的那样,您可能会遇到错误。