什么是unit testing?

我看到很多问题都是以单一的语言进行unit testing,而不是问“什么”,“为什么”和“何时”。

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

大体来说,unit testing是用testing代码独立地testing你的代码。 想到的直接优点是:

  • 运行testing变成可自动化和可重复的
  • 您可以通过GUI进行更细粒度的testing,而不是通过点击testing

请注意,如果您的testing代码写入文件,打开数据库连接或通过networking执行某些操作,则会更适当地将其分类为集成testing。 集成testing是一件好事,但不要与unit testing混淆。 unit testing代码应该是简短的,甜美且快速的执行。

另一种看unit testing的方法是先写testing。 这就是所谓的testing驱动开发(简称TDD)。 TDD带来额外的好处:

  • 你不会写投机“我将来可能需要这个”的代码 – 只要足够让testing通过
  • 你写的代码总是被testing覆盖
  • 通过首先编写testing,您不得不考虑如何调用代码,这通常会长期改善代码的devise。

如果你现在没有进行unit testing,我build议你开始吧。 得到一本好书,实际上任何xUnit-book都会做,因为它们之间的概念是非常可转换的。

有时写unit testing可能会很痛苦。 当这样做的时候,试着找人来帮助你,并且抵制“只写那该死的代码”的诱惑。 unit testing很像洗碗。 这并不总是令人愉快的,但它保持了你的隐喻厨房的清洁,你真的希望它是干净的。 🙂


编辑:一个误解浮现在脑海,虽然我不确定是否如此普遍。 我听说一个项目经理说,unit testing使团队编写了所有的代码两次。 如果这样看起来和感觉,那么你做错了。 编写testing通常不仅加快了开发速度,而且还为您提供了一个方便的“现在我已经完成”的指标,否则就不会有这个指标。

我不同意丹(虽然更好的select可能不会回答)…但…

unit testing是编写代码来testing系统行为和function的过程。

很明显,testing可以提高代码的质量,但这只是unit testing的一个肤浅的好处。 真正的好处是:

  1. 在确保不改变行为(重构)的同时更改技术实现更容易。 适当的unit testing代码可以积极地重构/清理,几乎没有发现任何事情的机会。
  2. 在添加行为或进行修复时为开发人员提供信心。
  3. logging你的代码
  4. 指出紧密耦合的代码区域。 很难unit testing紧密耦合的代码
  5. 提供一种方法来使用您的API并在早期寻找困难
  6. 指示不是非常内聚的方法和类

您应该进行unit testing,因为您有兴趣为您的客户提供可维护和高质量的产品。

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

最大的缺陷是开发人员testing过大的单位,或者他们认为一个单位的方法。 如果您不了解控制反转(Inversion of Control ),那么尤其如此,在这种情况下,您的unit testing将始终转化为端到端的集成testing。 unit testing应该testing个人行为 – 而且大多数方法都有很多行为。

最大的误解是程序员不应该testing。 只有不好或懒惰的程序员才会这样认为。 应该build立你的屋顶的家伙不testing它? 如果医生更换心脏瓣膜不检测新的瓣膜? 只有程序员可以testing他的代码是否符合他打算做的事情(QA可以testing边缘案例 – 当程序员被告知要执行某些操作时代码的行为,客户端可以进行验收testing – 代码是否执行客户为此付出了什么)

unit testing的主要区别在于它是自动的 ,因此是可重复的 ,而不是“只是打开一个新的项目并testing这个特定的代码”。

如果您手动testing您的代码,它可能会使您相信代码正在完美工作 – 处于当前状态 。 但是一个星期之后呢,当你稍作修改呢? 您是否愿意在代码中发生任何更改时再次手动重新testing? 最可能不是:-(

但是,如果您可以随时运行testing,只需点击一下,几秒钟内就可以完成相同的操作 ,那么只要出现问题,他们就会立即向您显示。 如果你也将unit testing整合到你的自动化构build过程中,即使在一个看起来完全不相关的变化破坏了代码库的某个部分的情况下,它们也会提醒你错误 – 当你甚至不知道需要重新testing该特定的function。

这是unit testing超越手工testing的主要优势。 但是等等,还有更多的:

  • unit testing显着地缩短了开发反馈循环 :如果单独testing部门,可能需要花费数周的时间才能知道代码中存在一个错误,那时候您已经忘记了大部分上下文,因此可能需要几个小时find并修复这个bug OTOH进行unit testing,反馈周期以秒为单位进行测量,错误修复过程通常是沿着“哦sh * t”的线条,我忘了在这里检查这个条件:-)
  • unit testing有效地logging (你的理解)你的代码的行为
  • unit testing迫使您重新评估您的deviseselect,从而导致更简单,更清洁的devise

unit testing框架反过来使您可以轻松地编写和运行testing。

我从来没有在大学里受过unit testing,而且花了我一段时间才得到它。 我读过它,去了“啊,对,自动testing,这可能是很酷我猜”,然后我忘了它。

花了相当长的时间才明白了这一点:比方说,你正在一个大系统上工作,然后你写一个小模块。 它编译,你通过它的步伐,它很好,你继续下一个任务。 九个月后,两个版本后,别人改变了一些看似不相关的程序部分,并打破了模块。 更糟糕的是,他们testing他们的变化,他们的代码工作,但他们不testing你的模块; 地狱,他们甚至可能不知道你的模块存在

现在你已经有了一个问题:破解的代码在主干中,甚至没有人知道。 最好的情况是内部testing人员在发货之前find它,但在游戏后期修复代码很昂贵。 如果没有内部testing人员发现它……那么确实会变得非常昂贵。

解决scheme是unit testing。 当你编写代码时,他们会遇到问题 – 这很好 – 但你可以亲自做。 真正的回报是,如果你现在正在做一个完全不同的项目,他们会在九个月后发现问题,但是一个暑期实习生认为如果这些参数是按字母顺序排列的话,它们会看起来更加整洁 – 然后unit testing你写回来失败,有人在实习生抛出的东西,直到他改变参数的顺序。 就是unit testing的“原因”。 🙂

关于unit testing和TDD的哲学优势,这里有一些他们关键的“灯泡”观察,这些观察让我对TDD启蒙之路上的初步尝试感到震惊(没有原始或必然的新闻)。

  1. TDD并不意味着写入两倍的代码量。 testing代码通常是相当快速和无痛的写入,并且是您的devise过程中关键的一部分。

  2. TDD帮助您了解何时停止编码! 你的testing让你相信自己现在已经做得足够了,可以停止调整,继续下一步。

  3. testing和代码一起工作以获得更好的代码。 你的代码可能是坏/马车。 你的testing可能是坏/马车。 在TDD中,您正在为这两个机会做坏事/坏车相当低的机会。 通常它的testing需要修复,但这仍然是一个好的结果。

  4. TDD有助于编码便秘。 你知道,你有这么多的感觉,你几乎不知道从哪里开始? 现在是星期五下午,如果你只是拖延了几个小时…… TDD允许你快速充实你认为你需要做的事情,并且让你的代码快速移动。 而且,就像实验鼠一样,我想我们都应对了这个大绿灯,更加努力地再次看到它!

  5. 类似的,这些devise器types可以看到他们在做什么。 他们可以漫步果汁/香烟/ iPhone的rest,并返回到显示器,立即给他们一个视觉提示,他们到了哪里。 TDD给了我们类似的东西。 当生活介入时,我们很容易看到我们到了哪里

  6. 我认为这是福勒说的:“不完美的testing,经常运行,比完全没有被写入的完美testing要好得多”。 我把这个解释为允许我编写testing,即使我的代码覆盖的其余部分是严重不完整的,我认为它们将是最有用的。

  7. TDD以各种令人惊讶的方式帮助解决问题。 良好的unit testing可以帮助logging什么是应该做的,他们可以帮助你将代码从一个项目迁移到另一个项目,并给你一个毫无根据的感觉,优于你的非testing同事:)

这个介绍是所有美味的善良testing需要的一个很好的介绍。

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

这是我的承担。 我会说unit testing是编写软件testing的做法,以validation您的真实软件是否做到了这一点。 这从Java世界中的jUnit开始,已经成为PHP中的最佳实践,同时也是SimpleTest和phpUnit的最佳实践。 这是极限编程的一个核心实践,可以帮助您确定在编辑之后,您的软件仍然按照预期工作。 如果您有足够的testing覆盖率,则可以进行重大的重构,缺陷修复或快速添加function,而不必担心引入其他问题。

所有的unit testing都可以自动运行,这是最有效的。

unit testing通常与OO开发有关。 基本的想法是创build一个脚本,为您的代码设置环境,然后运行它; 您编写断言,指定您应该接收的预期输出,然后使用上述框架来执行testing脚本。

该框架将针对您的代码运行所有testing,然后报告每个testing的成功或失败。 phpUnit默认从Linux命令行运行,尽pipe有可用的HTTP接口。 SimpleTest本质上是基于Web的,并且更容易起来和运行,IMO。 结合xDebug,phpUnit可以为您提供代码覆盖率的自动统计,有些人认为它非常有用。

有些团队从他们的Subversion版本库中编写钩子,以便在您提交更改时自动运行unit testing。

将unit testing保存在与您的应用程序相同的存储库中是一种很好的做法。

我使用unit testing来节省时间。

在构build业务逻辑(或数据访问)时,testingfunction通常可能涉及在很多可能完成或尚未完成的屏幕上input内容。 自动化这些testing可以节省时间。

对我来说,unit testing是一种模块化的testing工具。 每个公共职能通常至less有一个testing。 我写额外的testing来涵盖各种行为。

开发代码时所考虑的所有特例都可以在unit testing中的代码中logging下来。 unit testing也成为如何使用代码的例子。

对于我来说,发现我的新代码在unit testing中破坏了一些东西,然后检查代码,并让一些前端开发人员发现问题,速度要快得多。

对于数据访问testing,我尝试编写testing,要么没有变化,要么自行清理。

unit testing不能解决所有的testing需求。 他们将能够节省开发时间并testing应用程序的核心部分。

像NUnit , xUnit或JUnit这样的图书馆只是如果你想用Kent Beck推广的TDD方法开发你的项目,

您可以阅读“testing驱动开发(TDD)简介”或“Kent Beck的书” Test Driven Development:By Example“

然后,如果你想确保你的testing覆盖了代码的“好”部分,你可以使用像NCover , JCover , PartCover或其他软件。 他们会告诉你代码的覆盖率。 取决于你在TDD方面的熟练程度,你会知道你是否已经练习好了:)

unit testing是关于编写testing你的应用程序代码的代码。

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

xUnit有助于这个testing – 他们是帮助这个的框架。 其中一部分是自动化testing运行器,告诉你哪些testing失败,哪些testing失败。

他们还可以在每次testing之前设置通用代码,并在所有testing完成后将其撕下。

您可以进行一次testing,检查是否抛出了预期的exception,而无需自己编写整个try catch块。

我认为你不明白的一点是像NUnit(等)这样的unit testing框架将帮助你实现中小型testing的自动化 。 通常你可以在GUI中运行testing(例如NUnit就是这种情况),只需单击一个button,然后 – 希望 – 看到进度条保持绿色。 如果它变成红色,框架将显示哪个testing失败,究竟是什么错误。 在一个正常的unit testing中,你经常使用断言,例如Assert.AreEqual(expectedValue, actualValue, "some description") – 所以如果这两个值不相等,你会看到一个错误,说:“some description:expected <expectedValue> actualValue>”。

因此,作为一个结论,unit testing将使开发人员的testing更快,更舒适。 你可以在提交新的代码之前运行所有的unit testing,这样就不会中断同一个项目中其他开发者的构build过程。

使用Testivus 。 所有你需要知道的是在那里:)

unit testing是一种实践,以确保您要实现的function或模块将按预期行事(需求),并确保其在边界条件和无效input等情况下的行为。

xUnit , NUnit , mbUnit等是帮助您编写testing的工具。

unit testing是对代码单元(例如单一function)的testing,而不需要代码单元依赖的基础结构。 即隔离testing。

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

因此,一个unit testing将运行您正在testing的“函数”中所包含的function,而不会产生数据库更新的副作用。

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

在unit testing中,您只需要testing标准偏差的计算是否正确。 在集成testing中,您要testing标准偏差计算和数据库检索。

testing驱动开发有一个unit testing的术语。 作为一个老计时器,我会提到它的更通用的定义。

unit testing还意味着在一个更大的系统中testing单个组件。 这个单一的组件可能是一个DLL,EXE,类库等,甚至可能是一个多系统应用程序中的单一系统。 所以最终unit testing最终是对任何你想称之为一个更大的系统的testing。

然后,您可以通过testing所有组件如何一起工作来进行集成或系统testing。

首先,无论是unit testing还是任何其他types的自动化testing(集成,加载,UItesting等),与您所build议的主要区别在于它是自动化的,可重复的,并且不需要任何人力资源被消耗(=没有人必须执行testing,他们通常运行在一个button的按下)。

我在FoxForward 2007上进行了unit testing的演示,并被告知不要unit testing任何与数据一起工作的东西。 毕竟,如果您在实时数据上进行testing,那么结果是不可预测的,如果您不对实时数据进行testing,那么实际上并没有testing您编写的代码。 不幸的是,这是我最近编写的大部分代码。 🙂

最近我写了一个例程来保存和恢复设置,我在TDD上做了一些尝试。 首先,我validation了我可以创build存储对象。 那么,它有我需要调用的方法。 那么,我可以打电话给它。 然后,我可以传递参数。 然后,我可以通过它的具体参数。 等等,直到我最终validation它将保存指定的设置,允许我改变它,然后恢复它,几种不同的语法。

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

如果你得到一堆垃圾,你会怎么做,看起来像被困在一个永久的清理状态,你知道增加任何新的function或代码可以打破目前的设置,因为当前的软件就像一个房子牌?

那我们怎么做unit testing?

你从小开始。 我刚刚进入的这个项目直到几个月前才进行过unit testing。 当覆盖率很低时,我们只需select一个没有覆盖的文件,然后单击“添加testing”。

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

(最好的部分是,即使在这种低级别的覆盖范围内,我们已经遇到了许多错误代码的实例,testing也发现了这一点,这是推动人们添加更多testing的巨大动力。

这回答了你为什么要做unit testing。


下面的三个video覆盖了JavaScript中的unit testing,但一般原则适用于大多数语言。

unit testing:现在几分钟会节省时间 – Eric Mann – https://www.youtube.com/watch?v=_UmmaPe8Bzc

JSunit testing(非常好) – https://www.youtube.com/watch?v=-IYqgx8JxlU

编写可testing的JavaScript – https://www.youtube.com/watch?v=OzjogCFO4Zo


现在我正在学习这个主题,所以我可能不是100%正确的,除了我在这里描述的内容外,还有更多的内容,但是我对unit testing的基本理解是你写了一些testing代码(它和你的主代码),在主代码中调用函数所需的input(参数),然后代码检查是否返回有效的返回值。 如果它返回一个有效的值,那么你用来运行testing的unit testing框架会显示一个绿灯(全部是好的),如果这个值是无效的,那么你会得到一个红灯,然后你可以在你之前立即修复这个问题发布新的代码生产,没有testing你可能实际上没有发现错误。

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


另外事实上,你有一个运行你的代码的计算机程序,而不是你手动在浏览器中逐页进行testing,节省了时间(unit testing的JavaScript)。 假设您修改网页上的某个脚本所使用的函数,并且该函数对于其新的预期目的而言效果良好。 但是,让我们也为参数说,还有另一个函数,你有代码中的其他地方,取决于新修改的函数,以使其正常运行。 由于您对第一个函数所做的更改,此依赖函数现在可能会停止工作,但是,如果您的计算机未自动运行testing,则不会注意到该函数存在问题,直到它实际执行您必须手动导航到包含执行依赖函数的脚本的网页,然后才会注意到由于您对第一个函数所做的更改而存在错误。

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


当你第一次听到这个话题时,你觉得很困惑,我是不是已经在testing我的代码了? 而且你编写的代码已经像现在这样工作了,“为什么我需要另一个框架呢?”是的,你已经在testing你的代码了,但是一台计算机更好。 你只需编写足够好的testing代码的function/单元一次,其余的是由强大的CPU照顾你,而不是你必须手动检查你的代码仍然工作,当你做出改变你的代码。

另外,如果你不想要,你不必单位testing你的代码,但是随着你的项目/代码库开始变得越来越大,随着引入错误的机会增加,你的代码将会得到回报。

unit testing和TDD通常使您能够缩短您正在编写的软件的反馈周期。 而不是在实施的最后阶段有一个大的testing阶段,你逐渐testing你写的所有东西。 这会极大地提高代码质量,正如您立即看到的那样,您可能会遇到错误。