什么是单元测试?[关闭]


211

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

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

Uber有很多可用资源,Wikipedia文章无疑是一个不错的起点。一旦有了基本的想法,也许一些更具体的问题可以帮助您自己决定是否要使用单元测试。
古马


1
那不是1个具体问题。这是5个广泛的问题。
Raedwald 2013年

9
您问了这个问题是件好事,与阅读在线资源相比,阅读一个正在工作的程序员的答案要好得多,而且要点。
Pratyush Dhanuka,2014年

Answers:


197

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

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

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

查看单元测试的另一种方法是先编写测试。这就是所谓的测试驱动开发(TDD)。TDD具有其他优势:

  • 您无需编写推测性的“将来可能需要”代码,仅足以使测试通过
  • 您编写的代码始终包含在测试中
  • 通过首先编写测试,您被迫考虑如何调用代码,从长远来看,这通常可以改善代码的设计。

如果您现在不进行单元测试,建议您开始使用它。获得一本好书,几乎任何xUnit-book都可以做,因为这些概念之间可以很容易地转移。

有时编写单元测试会很痛苦。如果可以,请尝试找人帮助您,并抵制“只写该死的代码”的诱惑。单元测试很像洗碗。它并不总是那么令人愉悦,但是可以使您的隐喻厨房保持清洁,您真的希望它保持清洁。:)


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


2
您是否会提出关于TDD如何加速开发的观点?
马丁

70

我不同意Dan(尽管更好的选择可能只是不回答)...但是...

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

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

  1. 在确保不更改行为(重构)的同时,更轻松地更改技术实现。正确地进行了单元测试的代码可以被积极地重构/清除,几乎不会破坏任何内容而不会引起注意。
  2. 添加行为或进行修复时使开发人员充满信心。
  3. 记录您的代码
  4. 指示代码中紧密耦合的区域。很难对紧密耦合的代码进行单元测试
  5. 提供一种使用您的API并尽早发现困难的方法
  6. 指示不太紧密的方法和类

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

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

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

最大的误解是程序员不应进行测试。只有糟糕或懒惰的程序员才会相信这一点。盖屋顶的家伙不应该测试吗?更换心脏瓣膜的医生是否应该不测试新瓣膜?只有程序员才能测试他的代码是否达到了他的预期目的(QA可以测试极端情况-告诉人们要做程序员不想要的事情时代码的行为,客户可以进行验收测试-代码可以做到吗客户为此支付了什么费用)


44

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

如果您手动测试代码,则可能会说服您代码在当前状态下运行良好。但是大约一周后,如果您对其进行了一些修改,该怎么办?您是否愿意在代码中有任何更改时手动重新进行测试?可能不是:-(

但是,如果您可以在几秒钟之内随时以完全相同的方式单击一次测试,则只要出现问题,它们就会立即向您显示。而且,如果您还将单元测试集成到自动化的构建过程中,即使在看似完全不相关的更改在代码库的遥远部分中破坏了某些内容的情况下,它们也会提醒您注意错误-当您什至不会想到需要重新测试该特定功能。

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

  • 单元测试极大地缩短了开发反馈循环:使用单独的测试部门,您可能需要花费数周的时间才能知道代码中存在错误,而此时您已经忘记了大部分上下文,因此可能要花几个小时才能完成查找并修复错误;OTOH带有单元测试,反馈周期以秒为单位,并且错误修复过程通常遵循“哦,我* t,我忘了在这里检查这种情况”的意思:-)
  • 单元测试有效地记录(对您的理解)代码的行为
  • 单元测试迫使您重新评估设计选择,从而使设计更简单,更简洁

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


+1此外,我最喜欢的测试代码部分(尤其是在使用新的代码库时):演示了测试代码的预期用途。
史蒂文·埃弗斯

34

我从未在大学里教过单元测试,并且花了我一段时间才“得到”它。我读了一下,说“啊,对,自动化测试,我想那可能很酷”,然后我就忘记了。

我花了相当长的时间才真正弄清楚这一点:假设您在大型系统上工作,并且编写了一个小模块。它可以编译,通过它的步调,它可以很好地工作,然后继续执行下一个任务。下线了9个月,后来又发布了两个版本,其他人对程序中一些看似无关的部分进行了更改,从而中断了该模块。更糟糕的是,他们测试了自己的更改,代码也起作用,但是他们没有测试您的模块。地狱,他们甚至可能都不知道您的模块存在

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

解决方案是单元测试。当您编写代码时,它们会遇到问题-很好-但是您可以手工完成。真正的收获是,当您现在从事一个完全不同的项目时,它们将在9个月后发现问题,但夏季实习生认为,如果这些参数按字母顺序排列,则看起来会更整洁-然后进行单元测试您写的返回失败,并且有人在实习生扔东西,直到他改变了参数顺序。就是单元测试的“原因”。:-)


13

在这里,您将了解单元测试和TDD的哲学优势,其中一些是关键的“灯泡”观察,这使我震惊了我迈向TDD启蒙之路的初步尝试(无原创或必然的消息)...

  1. TDD并不意味着编写两倍的代码量。测试代码通常编写起来非常快捷,轻松,并且是设计过程中至关重要的一部分,并且至关重要。

  2. TDD可帮助您实现何时停止编码!通过测试,您可以确信自己已经做了足够的工作,并且可以停止调整并继续进行下一步。

  3. 测试和代码可以一起工作以获得更好的代码。您的代码可能是错误的/错误的。您的测试可能是错误/错误的。在TDD中,您希望同时降低不良率/越野车的可能性。通常,它的测试需要修复,但这仍然是一个很好的结果。

  4. TDD可帮助编码便秘。您知道自己有很多事情要做,几乎不知道从哪里开始吗?现在是星期五下午,如果您再拖延几个小时……TDD使您可以快速充实自己认为需要做的事情,并使代码快速运行。另外,就像实验室的老鼠一样,我认为我们所有人都对那巨大的绿灯做出了反应,并且努力工作才能再次看到它!

  5. 同样,这些设计人员类型可以查看他们正在从事的工作。他们可以漫步去喝果汁/抽烟/ iphone,然后回到监视器,该监视器立即为他们提供到达目的地的视觉提示。TDD给了我们类似的东西。当生活介入时,我们更容易看到去哪里...

  6. 我认为正是福勒说:“经常运行的不完美测试比根本没有编写的完美测试要好得多”。我认为这是允许我在我认为最有用的地方编写测试,即使余下的代码覆盖范围很不完整。

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

此演示文稿很好地介绍了所有可口的甜品测试所需要的内容。



5

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

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

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

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

对于我来说,发现我的新代码在单元测试中破坏了某些东西然后签入代码并让一些前端开发人员发现问题要快得多。

对于数据访问测试,我尝试编写不做任何更改或自行清理的测试。

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


4

这是我的看法。我会说单元测试是编写软件测试以验证您的真实软件是否达到了预期目的的一种做法。从Java世界中的jUnit开始,到PHP以及SimpleTestphpUnit都已成为最佳实践。这是极限编程的核心实践,可以帮助您确保软件在编辑后仍能按预期运行。如果您有足够的测试范围,则可以进行主要的重构,错误修复或快速添加功能,而不必担心引入其他问题。

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

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

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

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

最好将单元测试与应用程序放在同一存储库中。


4

如果要使用肯特·贝克(Kent Beck)流行的TDD方法来开发项目,则像NUnitxUnitJUnit这样的库只是强制性的:

您可以阅读“测试驱动开发简介(TDD)”或Kent Beck的书“ 测试驱动开发:示例”

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


3

单元测试是对代码单元(例如,单个功能)的测试,而无需该代码单元所依赖的基础结构。即单独进行测试。

例如,如果要测试的功能连接到数据库并进行更新,则在单元测试中,您可能不想执行该更新。如果是集成测试,您会这样做,但在这种情况下不是。

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

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

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


3

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

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

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

他们还具有在每次测试之前设置您在每个测试中需要的通用代码以及在所有测试完成后将其拆解的功能。

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


3

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

因此,结论是,单元测试将使测试更快,并使开发人员更舒适。您可以在提交新代码之前运行所有单元测试,以免破坏其他开发人员在同一项目上的构建过程。



3

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

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


2

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

单元测试还意味着测试大型系统中的单个组件。该单个组件可以是dll,exe,类库等。它甚至可以是多系统应用程序中的单个系统。因此,最终,单元测试最终将成为您要调用的大型系统中的任何一个的测试。

然后,您将通过测试所有组件的协同工作方式来进行集成或系统测试。


2

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


1

我在FoxForward 2007上参加了有关单元测试的演示,并被告知切勿对任何适用于数据的内容进行单元测试。毕竟,如果您对实时数据进行测试,结果将是不可预测的;并且,如果您不对实时数据进行测试,那么您实际上并不会在测试编写的代码。不幸的是,这是我最近做的大部分编码。:-)

最近,我在编写保存和恢复设置的例程时确实在TDD上拍摄过照片。首先,我确认可以创建存储对象。然后,它具有我需要调用的方法。然后,我可以称之为。然后,我可以传递参数。然后,我可以为其传递特定的参数。依此类推,直到我最终确认它可以保存指定的设置,然后允许我更改它,然后恢复它,以使用几种不同的语法。

我没有走到尽头,因为我需要立即执行常规操作,但这是一个很好的练习。


1

如果收到一堆废话,并且似乎陷入了永久性的清理状态,那么您该怎么办?您知道,添加任何新功能或代码都会破坏当前的清理状态,因为当前的软件就像一间屋子。牌?

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

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

目前,我们的比例已超过40%,并且我们设法摘下了大多数低挂的水果。

(最好的部分是,即使覆盖率很低,我们也已经遇到了很多代码实例在做错事,而测试却抓住了它。这是促使人们增加更多测试的巨大动力。)


1

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


下面的3个视频涵盖了使用javascript进行单元测试的过程,但是一般原则适用于大多数语言。

单元测试:分钟现在可以节省几个小时-埃里克·曼-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负责其余的工作,而无需在更改时手动检查所有代码是否仍在工作您的代码。

此外,如果您不想对代码进行单元测试,但随着引入错误的机会增加,项目/代码库开始变得更大时,它会有所回报。


0

通常,单元测试和TDD使您可以缩短有关所编写软件的反馈周期。您无需在实现的最后阶段进行大型测试,而可以逐步测试所编写的所有内容。正如您立即看到的那样,这可以极大地提高代码质量,在这些地方可能存在错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.