为啥使用 JUnit 进行测试?
Posted
技术标签:
【中文标题】为啥使用 JUnit 进行测试?【英文标题】:Why use JUnit for testing?为什么使用 JUnit 进行测试? 【发布时间】:2012-06-07 05:00:57 【问题描述】:也许我的问题是一个新手,但我不能真正理解我会在什么情况下使用junit?
无论我是编写简单的应用程序还是较大的应用程序,我都使用System.out
语句对其进行测试,这对我来说非常容易。
为什么要使用 JUnit 创建测试类,如果我们仍然需要调用相同的方法,在项目中创建不必要的文件夹,检查它们返回的内容,然后我们需要对所有内容进行注释的开销?
为什么不编写一个类并使用System.out
立即对其进行测试,但不创建测试类?
PS。我从未参与过我刚刚学习的大型项目。
那么目的是什么?
【问题讨论】:
阅读The Art of Unit Testing 您知道每次更改程序中的任何内容时,您之前手动检查输出的所有工作都将失效,您必须从头开始重做? 不仅仅是“测试”,“聪明的测试”也很重要。这是一个很好的例子:wp.me/prMeE-11 【参考方案1】:这不是测试,而是“手动查看输出”(在商业中称为 LMAO)。更正式地说,它被称为“手动查找异常输出”(LMFAO)。 (见下方注释)
无论何时更改代码,都必须针对受这些更改影响的所有代码运行应用和 LMFAO。即使在小型项目中,这也是有问题且容易出错的。
现在每次更改代码时都可以扩展到 50k、250k、1m LOC 或更多,以及 LMFAO。这不仅令人不快,而且是不可能的:您已经扩大了输入、输出、标志、条件的组合,并且很难运用所有可能的分支。
更糟糕的是,LMFAO 可能意味着访问网页应用程序的页面,运行报告,在数十个文件和机器上仔细研究数百万行日志,阅读生成和发送的电子邮件,检查短信,检查机器人的路径,填写一瓶苏打水,汇总来自一百个网络服务的数据,检查金融交易的审计线索……你明白了。 “输出”并不意味着几行文本,“输出”意味着聚合系统行为。
最后,单元和行为测试定义系统行为。测试可以由持续集成服务器运行并检查正确性。当然,System.out
s 也可以,但 CI 服务器不会知道其中一个是否有问题——如果有,它们就是单元测试,你不妨使用框架。
无论我们认为自己有多好,人类都不是好的单元测试框架或 CI 服务器。
注意:LMAO 正在测试,但在非常有限的意义上。它不能在整个项目或作为流程的一部分以任何有意义的方式重复。这类似于在 REPL 中增量开发,但从不将这些增量测试形式化。
【讨论】:
-1 表示第一句话,这是完全不真实的。【参考方案2】:JUnit 的主要优点是它是自动化的,而不是您必须手动检查打印输出。您编写的每个测试都保留在您的系统中。这意味着,如果您进行的更改产生了意想不到的副作用,您的测试将捕获它并失败,而不是您必须记住在每次更改后手动测试所有内容。
【讨论】:
【参考方案3】:当您使用 System.out 之类的东西进行测试时,您只是在测试一小部分可能的用例。当您处理可以接受几乎无限数量的不同输入的系统时,这不是很彻底。
单元测试旨在让您使用非常庞大且多样化的不同数据输入集在您的应用程序上快速运行测试。此外,最好的单元测试还考虑了边界情况,例如位于被认为有效的边缘的数据输入。
人类测试所有这些不同的输入可能需要数周时间,而机器可能需要几分钟。
这样想:你也不是在“测试”静态的东西。您的应用程序很可能会不断变化。因此,这些单元测试旨在在编译或部署周期的不同点运行。或许最大的优势在于:
如果您破坏了代码中的某些内容,您现在就会知道它,而不是在您部署之后,不是在 QA 测试人员发现错误时,也不是在您的客户取消时。您也将有更好的机会立即修复故障,因为很明显,破坏有问题的代码部分的事情很可能发生在您上次编译之后。因此,解决问题所需的调查工作量大大减少。
【讨论】:
【参考方案4】:我们编写测试来验证程序行为的正确性。
通过使用眼睛检查输出语句的内容来验证程序行为的正确性是手册,或者更具体地说,是视觉 过程。
你可以这么说
目视检查工作,我检查代码是否符合预期 做,对于这些情况,一旦我看到它是正确的,我们就可以 去吧。
现在首先,很高兴您对代码是否正常工作感兴趣。这是好事。你走在了曲线的前面!遗憾的是,这种方法存在问题。
目视检查的第一个问题是,如果您永远无法再次检查代码的正确性,那么您就是一个严重的焊接事故。
第二个问题是,使用的那双眼睛与眼睛主人的大脑紧密耦合。如果代码的作者还拥有视觉检查过程中使用的眼睛,那么验证正确性的过程依赖于视觉检查员大脑中内化的程序知识。
仅仅因为他们没有与原始编码人员的大脑合作,就很难有一双新的眼睛进来并验证代码的正确性。第二双眼睛的所有者必须与代码的原作者交谈才能完全理解相关代码。众所周知,对话作为一种分享知识的方式是不可靠的。如果原始编码器对新的眼睛不可用,则这一点没有实际意义。在那种情况下,新的眼睛必须阅读原始代码。
阅读其他人的单元测试未涵盖的代码比阅读具有相关单元测试的代码更难。最好的情况是阅读其他人的代码是一项棘手的工作,最坏的情况是这是软件工程中最艰巨的任务。雇主在宣传职位空缺时强调项目是新建项目(或全新项目)是有原因的。从头开始编写代码比修改现有代码更容易,从而使招聘的职位对潜在员工更具吸引力。
通过单元测试,我们将代码分成其组成部分。然后,对于每个组件,我们设置了我们的摊位,说明程序应该如何表现。每个单元测试都讲述了程序的该部分应如何在特定场景中运行的故事。每个单元测试就像合同中的一个条款,从客户端代码的角度描述应该发生的事情。
这意味着一双新的眼睛拥有两个关于相关代码的实时准确文档。
首先他们有代码本身,实现,代码是如何完成的;其次,他们拥有原始编码人员在一组正式陈述中描述的所有知识,这些陈述讲述了这段代码应该如何表现的故事。
单元测试捕获并正式描述了原作者在实现类时所拥有的知识。它们描述了该类在客户端使用时的行为方式。
您对这样做的有用性提出质疑是正确的,因为可能会编写无用的单元测试,不涵盖所有有问题的代码,变得陈旧或过时等等。我们如何确保单元测试不仅模仿而且改进了知识渊博、尽职尽责的作者在运行时直观地检查其代码的输出语句的过程?首先编写单元测试,然后编写代码以使该测试通过。完成后,让计算机运行测试,它们速度很快,它们擅长做重复性任务,它们非常适合这项工作。
每次触发他们测试的代码并为每个构建运行测试时,都要对其进行审查,从而确保测试质量。如果测试失败,请立即修复。
我们将运行测试的过程自动化,以便每次构建项目时都会运行它们。我们还自动生成代码覆盖率报告,详细说明测试覆盖和执行的代码百分比。我们争取高比例。如果一些公司没有编写足够的单元测试来描述代码行为的任何变化,他们会阻止将代码更改签入源代码控制。通常,第二双眼睛会与更改的作者一起审查代码更改。审阅者将检查更改,以确保更改可以理解并被测试充分覆盖。所以审查过程是手动的,但是当测试(单元和集成测试以及可能的用户验收测试)通过这个手动审查过程时,就成为自动构建过程的一部分。每次签入更改时都会运行它们。continuous-integration 服务器在构建过程中执行此任务。
自动运行的测试,维护代码行为的完整性,并有助于防止未来对代码库的更改破坏代码。
最后,提供测试可以让您积极地re-factor code,因为您可以安全地进行重大代码改进,因为您知道您的更改不会破坏现有测试。
Test Driven Development 有一个警告,那就是您编写代码时必须着眼于使其可测试。这涉及对接口进行编码并使用依赖注入等技术来实例化协作对象。查看Kent Beck 的工作,他很好地描述了 TDD。查找coding to interfaces并学习design-patterns
【讨论】:
【参考方案5】:我添加了一些其他 System.out 不能做的:
让每个测试用例独立(这很重要)
JUnit 可以做到:每次都会创建新的测试用例实例并调用 @Before
。
将测试代码与源代码分开
JUnit 可以做到。
与 CI 集成
JUnit 可以用 Ant 和 Maven 做到这一点。
轻松安排和组合测试用例
JUnit 可以做@Ignore
和测试套件。
轻松查看结果
JUnit 提供了许多 Assert 方法(assertEquals
、assertSame
...)
Mock 和 stub 让您专注于测试模块。
JUnit 可以做到:使用 mock 和 stub 让您设置正确的夹具,并专注于测试模块逻辑。
【讨论】:
【参考方案6】:JUnit 是 Java 编程语言的单元测试框架。它在测试驱动开发中很重要,并且是统称为 xUnit 的一系列单元测试框架之一。
JUnit 提倡“先测试后编码”的思想,强调为一段代码设置测试数据,可以先测试,再执行。这种方法就像“测试一点,编码一点,测试一点,编码一点……”,它提高了程序员的生产力和程序代码的稳定性,从而减少了程序员的压力和调试所花费的时间。
特点 JUnit 是一个开源框架,用于编写和运行测试。
提供注释以标识测试方法。
为测试预期结果提供断言。
为运行测试提供测试运行器。
JUnit 测试让您可以更快地编写代码,从而提高质量
JUnit 非常简单。它不那么复杂,花费的时间也更少。
JUnit 测试可以自动运行,它们会检查自己的结果并提供即时反馈。无需手动梳理测试结果报告。
JUnit 测试可以组织成包含测试用例甚至其他测试套件的测试套件。
Junit 在测试进度条中显示测试进度,如果测试正常,则显示为绿色,测试失败时显示为红色。
【讨论】:
【参考方案7】:单元测试可确保代码按预期工作。它们还非常有助于确保代码仍然按预期工作,以防您以后必须更改它以构建新功能来修复错误。对代码进行高测试覆盖率可以让您继续开发功能,而无需执行大量手动测试。
System.out
的手动方法很好,但不是最好的方法。这是您执行的一次性测试。在现实世界中,需求不断变化,并且大多数时候您对现有函数和类进行了大量修改。所以……不是每次你都测试已经写好的代码。
JUnit 中还有一些更高级的功能,例如
断言语句
JUnit 提供了测试某些条件的方法,这些方法通常以断言开始,并允许您指定错误消息、预期和实际结果
其中一些方法是
fail([message])
- 让测试失败。可用于检查代码的某个部分是否未到达。或者在实现测试代码之前进行失败的测试。
assertTrue(true)
/ assertTrue(false)
- 永远是真/假。如果测试尚未实施,可用于预定义测试结果。
assertTrue([message,] condition)
- 检查布尔值 condition
是否为真。
assertEquals([message,] expected, actual)
- 测试两个值是否相等(如果实现则根据equals
方法,否则使用==
参考比较)。注意:对于数组,检查的是引用,不是内容,使用assertArrayEquals([message,] expected, actual)
。
assertEquals([message,] expected, actual, delta)
- 测试两个浮点数或双精度值是否彼此相距一定距离,由delta
值控制。
assertNull([message,] object)
- 检查对象是否为空
等等。请参阅完整的 Javadoc 了解所有示例 here。
套房
使用测试套件,您可以在某种意义上组合多个测试类到一个单元中,这样您就可以一次执行它们。一个简单的例子,将测试类MyClassTest
和MySecondClassTest
组合成一个名为AllTests
的套件:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses( MyClassTest.class, MySecondClassTest.class )
public class AllTests
【讨论】:
【参考方案8】:JUNIT 是java 开发者通常接受的方法。 他们可以为函数提供类似的预期输入,并据此决定编写的代码是完美编写的,或者如果测试用例失败,则可能还需要实施不同的方法。 JUNIT将加快开发速度,确保功能0缺陷。
【讨论】:
【参考方案9】:我对为什么需要 JUnit 的看法略有不同。
您实际上可以自己编写所有测试用例,但这很麻烦。以下是问题:
我们可以添加 if(value1.equals(value2))
而不是 System.out
并返回 0 或 -1 或错误消息。在这种情况下,我们需要一个“主”测试类来运行所有这些方法并检查结果并维护哪些测试用例失败和哪些通过。
如果您想添加更多测试,您还需要将它们添加到这个“主”测试类中。对现有代码的更改。如果你想从测试类中自动检测测试用例,那么你需要使用反射。
Eclipse 未检测到您的所有测试和运行测试的主类,您需要编写自定义调试/运行配置来运行这些测试。不过,您仍然看不到那些漂亮的绿色/红色输出。
这是 JUnit 正在做的事情:
它具有assertXXX()
方法,可用于从条件打印有用的错误消息并将结果传达给“主”类。
“main”类叫做runner,是JUnit提供的,所以不用写。它通过反射自动检测测试方法。如果您添加带有 @Test
注释的新测试,则会自动检测到它们。
JUnit 还集成了 eclipse 和 maven/gradle 集成,因此运行测试很容易,您无需编写自定义运行配置。
我不是 JUnit 方面的专家,所以这是我目前所理解的,将来会添加更多。
【讨论】:
我猜你在第一部分写了如果没有 JUnit 来使单元测试比 system.out.println 语句好一点,我们会做些什么。可能JUnit是一些程序员这种尝试的结果,他们觉得需要编写一个单独的测试框架来执行这种自动化,因此JUnit诞生了,可能是。【参考方案10】:你不能在不使用测试框架的情况下编写任何测试用例,否则你将不得不编写你的测试框架来公正地处理你的测试用例。 这里有一些关于 JUnit 框架的信息,除了你可以使用 TestNG 框架。
什么是 Junit?
Junit 是与 Java 编程语言一起被广泛使用的测试框架。您可以将此自动化框架用于单元测试和 UI 测试。它可以帮助我们使用不同的注释定义代码的执行流程。 Junit 建立在“先测试后编码”的理念之上,这有助于我们提高测试用例的生产力和代码的稳定性。
Junit 测试的重要特性 -
-
它是一个开源测试框架,允许用户有效地编写和运行测试用例。
提供各种类型的注释来识别测试方法。
提供不同类型的断言来验证测试用例的执行结果。
它还为测试运行器提供了有效运行测试的方法。
它非常简单,因此可以节省时间。
它提供了以测试套件的形式组织测试用例的方法。
它以简单优雅的方式提供测试用例结果。
您可以将 jUnit 与 Eclipse、android Studio、Maven & Ant、Gradle 和 Jenkins 集成
【讨论】:
【参考方案11】:JUNIT:观察和调整
这是我对 JUNIT 的看法。
JUNIT可以用来, 1)在系统中添加新单元时观察系统行为。 2)对系统进行调整,迎接系统中的“新”单元。 什么?没错。
现实生活例如。
当您的亲戚参观您的大学宿舍时, 1)你会假装更负责任。 2)你会把所有东西放在它们应该放在的地方,比如鞋架上的鞋子不在椅子上,衣橱里的衣服不在椅子上。 3)您将摆脱所有违禁品。 4)您将在您拥有的每台设备中开始清理。
在编程方面
系统:您的代码 单位:新功能。 由于 JUNIT 框架用于 JAVA 语言,所以 JUNIT = JAVA UNIT (可能是)。
假设你已经有一个很好的防弹代码,但是一个新的需求来了,你必须在你的代码中添加新的需求。这个新要求可能会破坏您的某些输入(测试用例)的代码。
适应这种变化的简单方法是使用单元测试 (JUNIT)。 为此,您应该在构建代码库时为您的代码编写多个测试用例。每当有新需求出现时,您只需运行所有测试用例以查看是否有任何测试用例失败。 如果否,那么您是 BadA** 艺术家,您已准备好部署新代码。 如果任何测试用例失败,那么您更改代码并再次运行测试用例,直到您获得绿色状态。
【讨论】:
以上是关于为啥使用 JUnit 进行测试?的主要内容,如果未能解决你的问题,请参考以下文章
为啥使用Junit Test而不用普通java main方法来完成测试?
JUnit:通过 eclipse 和 maven 测试运行。速度差别很大,为啥?
为啥我的 Bean 在 Junit 测试中没有资格成为 Autowire 候选人?