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

  • 它是什么?
  • 它对我有什么作用?
  • 我为什么要使用它?
  • 我什么时候应该使用它(以及什么时候不应该使用它)?
  • 有哪些常见的陷阱和误解
有帮助吗?

解决方案

粗略地说,单元测试是与测试代码隔离地测试代码的各个部分。我想到的直接优势是:

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

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

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

  • 你不会编写推测性的“我将来可能需要这个”代码——足以让测试通过
  • 您编写的代码始终被测试覆盖
  • 通过首先编写测试,您被迫考虑如何调用代码,从长远来看,这通常会改进代码的设计。

如果您现在不进行单元测试,我建议您开始进行。找一本好书,几乎任何 xUnit 书籍都可以,因为这些概念在它们之间可以很好地转移。

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


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

其他提示

我不同意丹的观点(虽然更好的选择可能就是不回答)……但是……

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

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

  1. 使更改技术实现变得更容易,同时确保不更改行为(重构)。经过适当单元测试的代码可以被积极地重构/清理,几乎不可能在不注意的情况下破坏任何东西。
  2. 让开发人员在添加行为或进行修复时充满信心。
  3. 记录您的代码
  4. 指出代码中紧密耦合的区域。很难对紧密耦合的代码进行单元测试
  5. 提供一种使用 API 并尽早发现困难的方法
  6. 表示方法和类的内聚性不是很强

您应该进行单元测试,因为向客户提供可维护的优质产品符合您的利益。

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

最大的陷阱是开发人员测试太大的单元,或者他们将方法视为一个单元。如果你不明白的话尤其如此 控制反转 - 在这种情况下,您的单元测试将始终变成端到端集成测试。单元测试应该测试个体行为——大多数方法都有很多行为。

最大的误解是程序员不应该进行测试。只有糟糕或懒惰的程序员才会相信这一点。给你盖屋顶的人不应该测试一下吗?更换心脏瓣膜的医生不应该测试新瓣膜吗?只有程序员才能测试他的代码是否按照他的意图进行(QA 可以测试边缘情况 - 当代码被告知做程序员不打算做的事情时,代码的行为方式,并且客户端可以进行验收测试 - 代码是否做了客户付钱做什么)

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

如果您手动测试代码,它可能会让您相信代码运行良好 - 在目前的状态下. 。但是一周后,当你对其进行轻微修改时呢?您是否愿意在任何时候再次手动重新测试 任何事物 你的代码有变化吗?很可能不是:-(

但如果你可以 只需单击一下即可随时以完全相同的方式在几秒钟内运行测试, , 然后他们 将要 每当有东西损坏时立即向您显示。而且,如果您还将单元测试集成到自动化构建过程中,即使看似完全不相关的更改破坏了代码库较远部分的某些内容,它们也会向您发出错误警报 - 而您甚至不会想到有需要重新测试该特定功能。

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

  • 单元测试 缩短开发反馈循环 戏剧性地:如果有一个单独的测试部门,您可能需要几周的时间才能知道代码中存在错误,此时您已经忘记了大部分上下文,因此可能需要几个小时才能找到并修复错误;OTOH 使用单元测试,反馈周期以秒为单位测量,并且错误修复过程通常类似于“哦,天啊,我忘了检查这里的情况”:-)
  • 有效地进行单元测试 文档 (您对)代码行为的理解
  • 单元测试迫使您重新评估您的设计选择,这会导致 更简单、更简洁的设计

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

我在大学里从来没有学过单元测试,我花了一段时间才“掌握”它。我读到了它,然后“啊,对了,自动化测试,我猜这可能很酷”,然后我就忘了它。

我花了相当长的时间才真正弄清楚要点:假设您正在开发一个大型系统并编写一个小模块。它编译了,你让它通过它的步伐,它工作得很好,你就可以继续下一个任务。九个月后,两个版本后,其他人对某些内容进行了更改 似乎 程序的不相关部分,并且它破坏了模块。更糟糕的是,他们测试了他们的更改,并且他们的代码可以工作,但他们不测试你的模块;天哪,他们甚至可能不知道你的模块 存在.

现在你遇到了一个问题:损坏的代码在后备箱里,甚至没有人知道。最好的情况是内部测试人员在您发布之前找到它,但在游戏后期修复代码的成本很高。如果没有内部测试人员发现它......那么,这确实会变得非常昂贵。

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

这里讨论单元测试和 TDD 的哲学优点,其中一些关键的“灯泡”观察结果让我在迈向 TDD 启蒙之路上尝试性的第一步时感到震惊(不是原创的,也不一定是新闻)......

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

  2. TDD 帮助您意识到何时停止编码!您的测试让您相信您现在已经做得足够多了,可以停止调整并继续进行下一件事。

  3. 测试和代码一起工作以实现更好的代码。你的代码可能很糟糕/有缺陷。你的测试可能很糟糕/有问题。在 TDD 中,你指望两者都不好/有 bug 的可能性相当低。通常这是需要修复的测试,但这仍然是一个好的结果。

  4. TDD 有助于解决编码便秘问题。您知道那种感觉,您有太多事情要做,却几乎不知道从哪里开始吗?现在是星期五下午,如果你再拖延几个小时的话……TDD 可以让您快速充实您认为需要做的事情,并让您的编码快速进行。另外,就像实验室的老鼠一样,我认为我们都会对那盏大绿灯做出反应,并更加努力地再次看到它!

  5. 同样,这些设计师类型可以看到他们正在做什么。他们可以出去喝杯果汁/抽烟/玩手机休息一下,然后返回显示器,显示器会立即向他们提供他们到达哪里的视觉提示。TDD 给了我们类似的东西。当生活介入时,我们更容易看到我们走到了哪里......

  6. 我认为是福勒说的:“经常运行的不完美测试比从未编写过的完美测试要好得多”。我将此解释为允许我编写测试,即使我的代码覆盖范围的其余部分非常不完整,我认为它们也是最有用的。

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

本次演讲 是对所有美味品质测试的精彩介绍。

我想推荐 Gerard Meszaros 写的《xUnit 测试模式》一书。它很大,但却是单元测试的重要资源。这是他的网站的链接,他在其中讨论了单元测试的基础知识。 http://xunitpatterns.com/XUnitBasics.html

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

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

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

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

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

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

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

这是我的看法。我想说单元测试是编写软件测试来验证您的真实软件是否达到预期目的的实践。这开始于 j单位 在 Java 世界中,并且已成为 PHP 的最佳实践 简单测试php单元. 。这是极限编程的核心实践,可以帮助您确保您的软件在编辑后仍然按预期运行。如果您有足够的测试覆盖率,您可以进行重大重构、错误修复或快速添加功能,而不必担心引入其他问题。

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

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

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

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

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

图书馆喜欢 单位, x单位 或者 联合单元 如果您想使用 时分双工 Kent Beck 推广的方法:

你可以阅读 测试驱动开发 (TDD) 简介 或肯特·贝克的书 测试驱动开发:举例说明.

然后,如果您想确保测试覆盖代码的“良好”部分,您可以使用类似的软件 NC覆盖, 杰盖, 零件盖 管他呢。他们会告诉您代码的覆盖率。根据您对 TDD 的熟练程度,您就会知道自己是否练习得足够好:)

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

例如,如果您正在测试的函数连接到数据库并执行更新,则在单元测试中您可能不想执行该更新。如果这是集成测试,您会这样做,但在本例中不是。

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

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

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

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

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

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

他们还可以预先设置每个测试中所需的通用代码,并在所有测试完成后将其拆除。

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

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

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

使用 睾丸. 。您需要知道的一切都在那里:)

单元测试是一种实践,旨在确保您要实现的功能或模块将按预期(要求)运行,并确保它在边界条件和无效输入等场景中的行为方式。

x单位, 单位, mb单位, , ETC。是帮助您编写测试的工具。

测试驱动开发有点取代了单元测试这个术语。作为一个老前辈,我会提到它的更通用的定义。

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

然后,您可以通过测试所有组件如何协同工作来进行集成或系统测试。

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

我参加了 FoxForward 2007 的单元测试演示,并被告知永远不要对任何与数据相关的内容进行单元测试。毕竟,如果您对实时数据进行测试,结果是不可预测的,如果您不对实时数据进行测试,那么您实际上并没有测试您编写的代码。不幸的是,这就是我这些天所做的大部分编码。:-)

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

我没有坚持到最后,因为我现在需要例行公事,但这是一个很好的练习。

如果你收到一堆垃圾,并且看起来你陷入了永久的清理状态,你知道添加任何新功能或代码都可能破坏当前的设置,因为当前的软件就像一座房子,你会怎么做?牌?

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

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

现在我们已经完成了 40% 以上,并且我们已经成功地摘取了大部分容易实现的目标。

(最好的部分是,即使在如此低的覆盖范围内,我们也已经遇到了许多代码实例执行错误的操作,并且测试发现了它。这是推动人们添加更多测试的巨大动力。)

这回答了为什么你应该进行单元测试。


下面的 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 的单元测试)。假设您修改了网页上某个脚本所使用的函数,并且该函数运行良好且适合其新的预期目的。但是,为了论证起见,我们还可以说,代码中其他地方还有另一个函数,它依赖于新修改的函数才能正常运行。由于您对第一个函数所做的更改,此依赖函数现在可能会停止工作,但是如果没有计算机自动运行的测试,您将不会注意到该函数存在问题,直到它实际执行并且您必须手动导航到包含执行依赖函数的脚本的网页,然后您才会注意到由于对第一个函数所做的更改而存在错误。

重申一下,在开发应用程序时运行测试会在编码时发现此类问题。如果没有适当的测试,您就必须手动检查整个应用程序,即使如此,也很难发现错误,您天真地将其发送到生产中,过了一会儿,一个善良的用户向您发送了一份错误报告(其中不会像测试框架中的错误消息那么好)。


当您第一次听到这个主题时,您会感到非常困惑,并且您会想,我不是已经在测试我的代码了吗?您编写的代码已经像预期的那样工作了,“为什么我需要另一个框架?”......是的,您已经在测试您的代码,但计算机更擅长这样做。你只需要为一个函数/代码单元编写一次足够好的测试,剩下的就由强大的 cpu 为你处理,而不必在你进行更改时手动检查所有代码是否仍在工作。你的代码。

另外,如果您不愿意,则不必对代码进行单元测试,但随着引入错误的机会增加,您的项目/代码库开始变得更大,它会得到回报。

一般来说,单元测试和 TDD 可以让您缩短正在编写的软件的反馈周期。您不必在实施的最后阶段进行大型测试,而是逐步测试您编写的所有内容。这极大地提高了代码质量,正如您立即看到的那样,您可能会在其中存在错误。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top