为什么要使用JUnit进行测试?


131

也许我的问题是新手,但是我无法真正理解在什么情况下使用

无论是编写简单的应用程序还是大型应用程序,我都使用System.out语句对其进行测试,这对我来说很容易。

如果仍然需要调用相同的方法,检查它们返回的内容,然后注释所有内容,那么为什么还要使用JUnit创建测试类,在项目中创建不必要的文件夹呢?

为什么不编写一个类并立即对其进行System.out测试却不创建Test-class?

PS。我从未在刚刚学习的大型项目中工作过。

那么目的是什么?



7
您知道,每次在程序中进行任何更改时,以前检查输出的所有工作都会失效,您是否必须从头开始重做?
托尔比约恩Ravn的安徒生

不仅“测试”,而且“智能测试”也非常重要。这是一个很好的例子:wp.​​me/prMeE-11
akcasoy 2015年

Answers:


138

那不是测试,而是“手动查看输出”(在企业中称为LMAO)。更正式地说,它被称为“手动查找异常输出”(LMFAO)。(请参阅下面的注释)

每次更改代码时,都必须为受这些更改影响的所有代码运行应用程序和LMFAO。即使在小型项目中,这也是有问题且容易出错的。

现在,您可以在每次更改代码时扩展到50k,250k,1m LOC或更高,以及LMFAO。这不仅令人不快,而且是不可能的:您已经放大了输入,输出,标志,条件的组合,并且很难行使所有可能的分支。

更糟糕的是,LMFAO可能意味着访问Web应用程序的页面,运行报告,遍历数十个文件和机器的数百万条日志行,阅读生成和发送的电子邮件,检查文本消息,检查机器人的路径,装满一瓶苏打水,从一百个Web服务中聚合数据,检查财务交易的审计记录……您就明白了。“输出”并不意味着几行文字,“输出”意味着聚合的系统行为。

最后,单元测试和行为测试定义了系统行为。测试可以由连续集成服务器运行,并检查其正确性。当然可以System.out,但是CI服务器不会知道其中之一是否错误-如果确实出错,则说明它们是单元测试,因此您最好使用框架。

无论我们认为自己有多好,人类都不是好的单元测试框架或CI服务器。


注意:LMAO 正在测试,但意义非常有限。在整个项目中或作为过程的一部分,它不能以任何有意义的方式重复。这类似于在REPL中进行增量开发,但从不对那些增量测试进行形式化。


3
-1表示第一句话,这完全是完全错误的。
Michael Borgwardt

50

我们编写测试来验证程序行为的正确性。

通过用眼睛检查输出语句的内容来验证程序行为的正确性是手动的,或更具体地说,是视觉过程。

你可以说

目视检查工作正常,对于这些情况,我检查代码是否按其意图执行,一旦我看到正确,我们就可以开始了。

现在首先,您对代码是否正确运行感兴趣,这对您很重要。这是好事。您领先于弯道!可悲的是,这种方法存在问题。

目视检查的第一个问题是,您将是一次严重的焊接事故,而又无法再次检查代码的正确性。

第二个问题是所使用的那双眼睛与眼睛所有者的大脑紧密相连。如果代码的作者还拥有目视检查过程中使用的眼睛,则验证正确性的过程将依赖于目视检查员大脑中内部化的程序知识。

仅仅因为它们没有与原始编码器的大脑配合,新的眼睛就很难进入并验证代码的正确性。为了完全理解所讨论的代码,第二只眼睛的所有者必须与代码的原始作者进行交谈。众所周知,将对话作为分享知识的手段是不可靠的。如果新编码器的双眼无法使用原始编码器,这是没有意义的。在这种情况下,新的眼睛必须阅读原始代码。

读取单元测试未涵盖的其他人的代码比读取具有关联的单元测试的代码更困难。最好的方式是阅读别人的代码是棘手的工作,最糟糕的是,这是软件工程中最棘手的任务。雇主在发布职位空缺广告时,有一个理由强调,一个项目是未开发的(或全新的)项目。从头开始编写代码比修改现有代码容易,因此使招聘广告看起来对潜在员工更具吸引力。

通过单元测试,我们将代码分为其组成部分。然后,对于每个组件,我们都要列出档位,说明程序如何运行。每个单元测试都讲述了该部分程序在特定情况下应如何执行的故事。每个单元测试就像合同中的条款,从客户端代码的角度描述应该发生的情况。

这就意味着,一个新的一双眼睛有2的股实时和准确的有问题的代码文档。

首先,他们拥有代码本身,实现,代码完成方式;其次,他们具有所有知识,即原始编码员在一组正式语句中描述了这些正式语句,从而说明了该代码如何工作

单元测试捕获并正式描述了原始作者实施课程时所拥有的知识。它们提供了有关该类在客户端使用时的行为的描述。

您正确地质疑这样做的用处,因为有可能编写无用的单元测试,未涵盖所有有问题的代码,变得陈旧或过时等等。我们如何确保单元测试不仅可以模仿,而且可以改善知识渊博,认真负责的作者在运行时直观地检查其代码的输出语句的过程?首先编写单元测试,然后编写代码以使该测试通过。完成后,让计算机运行测试,它们非常快,非常擅长执行重复的任务,非常适合这项工作。

每次触发测试代码并为每个内部版本运行测试时,请通过检查它们来确保测试质量。如果测试失败,请立即修复。

我们使运行测试的过程自动化,以便每次我们构建项目时都运行它们。我们还自动生成代码覆盖率报告,该报告详细说明了测试覆盖和执行的代码百分比。我们追求高比例。如果某些公司没有编写足够的单元测试来描述代码行为的任何变化,则某些公司将阻止将代码更改签入源代码控制。通常,另一双眼睛会与更改的作者一起查看代码更改。审阅者将仔细检查更改,以确保测试可以理解并充分覆盖了更改。因此,审核过程是手动的,但是当测试(单元和集成测试以及可能的用户验收测试)通过此手动检查过程时,它将成为自动构建过程的一部分。每次签入更改时都会运行它们。A 服务器将在构建过程中执行此任务。

自动运行的测试可以维护代码行为的完整性,并有助于防止将来对代码库的更改破坏代码

最后,提供测试可以使您积极地重构代码,因为您可以在知道更改不会破坏现有测试的情况下安全地进行大型代码改进。

测试驱动开发有一个警告,那就是您必须编写代码以使其可测试。这涉及到对接口进行编码并使用诸如依赖注入之类的技术来实例化协作对象。查看肯特·贝克Kent Beck)的工作,他很好地描述了TDD。查找接口编码并进行研究


13

当您使用诸如System.out之类的东西进行测试时,您仅在测试一小部分可能的用例。当您要处理可以接受几乎无限数量的不同输入的系统时,这不是很彻底。

单元测试旨在使您能够使用大量不同的数据输入集在应用程序上快速运行测试。此外,最佳的单元测试还考虑了边界情况,例如恰好位于有效边缘的数据输入。

对于一个人来说,测试所有这些不同的输入可能需要数周的时间,而对于一台机器来说则可能需要数分钟。

这样想:您也不是在“测试”静态的东西。您的应用程序很可能会不断变化。因此,这些单元测试旨在在编译或部署周期的不同点运行。也许最大的好处是:

如果您在代码中碰坏,你就会知道这件事,现在,你不部署后,没有当QA测试仪捕获的错误,而不是当你的客户已经取消。您还将有更好的机会立即修复故障,因为很明显,破坏所讨论代码部分的事情很可能是自上次编译以来发生的。因此,大大减少了解决该问题所需的调查工作量。


9

我添加了其他一些System.out无法执行的操作:

  • 使每个测试用例独立(很重要)

    JUnit可以做到:每次将创建并@Before调用新的测试用例实例时。

  • 将测试代码与源代码分开

    JUnit可以做到。

  • 与CI整合

    JUnit可以使用Ant和Maven做到这一点。

  • 轻松安排和合并测试用例

    JUnit可以做@Ignore和测试套件。

  • 容易检查结果

    JUnit提供了许多Assert方法(assertEqualsassertSame...)

  • 模拟和存根使您可以专注于测试模块。

    JUnit可以做到:使用模拟和存根使您设置正确的灯具,并专注于测试模块逻辑。


9

单元测试确保代码按预期工作。它们对于确保代码仍然按预期工作仍然很有帮助,以防万一您以后需要更改代码以构建新功能来修复错误时使用。对代码进行高测试覆盖率使您可以继续开发功能,而无需执行大量手动测试。

您的手动方法System.out是好的,但不是最好的方法。这是您执行的一次测试。在现实世界中,需求不断变化,并且在大多数情况下,您需要对现有函数和类进行大量修改。因此,……并非每次都测试已经编写的代码。

JUnit中还有一些更高级的功能,例如

断言

JUnit提供了用于测试某些条件的方法,这些方法通常以断言开头,并允许您指定错误消息,预期结果和实际结果。

其中一些方法是

  1. fail([message])-让测试失败。可能用于检查未达到代码的特定部分。或者在实施测试代码之前进行失败的测试。
  2. assertTrue(true)/ assertTrue(false)-永远是真/假。如果尚未实施测试,则可用于预定义测试结果。
  3. assertTrue([message,] condition)-检查布尔值condition是否正确。
  4. assertEquals([message,] expected, actual)-测试两个值是否相等(根据实现的equals方法,否则使用==参考比较)。注意:对于数组,使用的是检查的引用,而不是内容assertArrayEquals([message,] expected, actual)
  5. assertEquals([message,] expected, actual, delta)-测试两个float值或double值是否彼此相距一定距离,并由该delta值控制。
  6. assertNull([message,] object) -检查对象是否为空

等等。看到所有示例的完整的Javadoc 在这里

套房

使用测试套件,从某种意义上讲,您可以多个测试类合并到一个单元中,以便可以一次执行所有测试类。一个简单的例子,测试类组合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 { } 

6

JUnit的主要优点是它是自动化的,而不需要您手动检查打印输出。您编写的每个测试都保留在系统中。这意味着,如果您进行的更改具有意外的副作用,则您的测试将被捕获并失败,而不必记住每次更改后都必须手动测试所有内容。


4

JUnit是Java编程语言的单元测试框架。它在测试驱动的开发中很重要,并且是一个统称为xUnit的单元测试框架系列之一。

JUnit提倡“先测试然后编码”的思想,该思想强调为先进行测试然后可以实现的代码设置测试数据。这种方法就像“一点测试,一点代码,一点测试,一点代码...”,这提高了程序员的工作效率和程序代码的稳定性,从而减少了程序员的压力和调试时间。

功能JUnit是一个开放源代码框架,用于编写和运行测试。

提供注释以标识测试方法。

提供用于测试预期结果的断言。

提供运行测试的测试运行器。

JUnit测试使您可以更快地编写代码,从而提高质量

JUnit非常优雅。它不那么复杂,所需的时间也更少。

JUnit测试可以自动运行,它们可以检查自己的结果并提供即时反馈。无需手动梳理测试结果报告。

JUnit测试可以组织到包含测试用例甚至其他测试套件的测试套件中。

如果测试正常,Junit会在绿色栏中显示测试进度,如果测试失败,它将变为红色。


2

对于为什么需要JUnit,我有不同的看法。

您实际上可以自己编写所有测试用例,但这很麻烦。这里是问题:

  1. 代替System.out我们可以添加if(value1.equals(value2))并返回0或-1或错误消息。在这种情况下,我们需要一个“主”测试类,该类运行所有这些方法并检查结果,并维护哪些测试用例失败以及哪些测试用例通过。

  2. 如果要添加更多测试,则还需要将它们添加到该“主”测试类中。更改现有代码。如果要自动从测试类中检测测试用例,则需要使用反射。

  3. Eclipse不会检测到所有测试和运行测试的主要类,因此您需要编写自定义调试/运行配置才能运行这些测试。您仍然看不到那些漂亮的绿色/红色输出。

这是JUnit在做什么:

  1. 它具有一些assertXXX()方法,这些方法可用于从条件中打印有用的错误消息并将结果传达给“主”类。

  2. “主要”类称为JUnit提供的运行程序,因此我们不必编写任何类。并通过反射自动检测测试方法。如果添加带有@Test注解的新测试,则会自动检测到它们。

  3. JUnit还具有eclipse集成和maven / gradle集成,因此它很容易运行测试,并且您不必编写自定义运行配置。

我不是JUnit专家,所以到目前为止,这是我目前所了解的内容,将来还会增加更多内容。


我猜在第一部分中,您写了如果不存在JUnit以使单元测试比system.out.println语句更好的话,我们会做些什么。JUnit可能是某些程序员进行此类尝试的结果,他们认为有必要编写一个单独的测试框架来实现这种自动化,因此JUnit诞生了。
Saurabh Patil

1

您必须在不使用测试框架的情况下编写任何测试用例,否则您将不得不编写测试框架以使测试用例公道。除了可以使用TestNG framework之外,这里还有一些有关JUnit Framework的信息。

什么是Junit?

Junit与Java编程语言一起被广泛使用的测试框架。您可以将此自动化框架用于单元测试和UI测试,这有助于我们定义具有不同注释的代码的执行流程。Junit建立在“先测试然后编码”的思想上,这有助于我们提高测试用例的生产率和代码的稳定性。

Junit测试的重要功能-

  1. 它是一个开放源代码测试框架,允许用户有效地编写和运行测试用例。
  2. 提供各种类型的注释以标识测试方法。
  3. 提供不同的断言类型来验证测试用例的执行结果。
  4. 它还为测试运行人员提供了有效运行测试的方法。
  5. 这非常简单,因此可以节省时间。
  6. 它提供了以测试服形式组织测试用例的方法。
  7. 它以简单而优雅的方式给出了测试用例的结果。
  8. 您可以将jUnit与Eclipse,Android Studio,Maven&Ant,Gradle和Jenkins集成

0

JUNIT是Java开发人员通常接受的方法。当他们可以向函数提供相似的预期输入并相应地确定编写的代码是否完美编写时,或者如果测试用例失败,则可能还需要实现不同的方法。JUNIT将加快开发速度,并确保功能中的0缺陷。


0

JUNIT:观察与调整

这是我对JUNIT的看法。

JUNIT可用于:
1)在系统中添加新单元时,观察系统行为。
2)在系统中进行调整以欢迎系统中的“新”单元。
什么?究竟。

现实生活

当您的亲戚到您的大学宿舍房间时,
1)您将假装更负责任。
2)您将所有物品放置在应有的位置,例如鞋架中的鞋子不在椅子上,橱柜中的衣服不在椅子上。
3)您将摆脱所有违禁品。
4)您将在拥有的每个设备中启动cleanUp。

用编程术语

系统:您的代码
UNIT:新功能。
由于JUNIT框架用于JAVA语言,因此JUNIT = JAVA UNIT(可能是)。

假设您已经有一个很好的防弹代码,但是有一个新的要求,您必须在代码中添加新的要求。这项新要求可能会破坏某些输入(测试用例)的代码。

适应这种变化的简单方法是使用单元测试(JUNIT)。
为此,在构建代码库时,应为代码编写多个测试用例。每当有新要求出现时,您都只需运行所有测试用例,以查看是否有任何测试用例失败。如果否,那么您是BadA **艺术家,就可以部署新代码了。
如果任何一个测试用例失败,则可以更改代码,然后再次运行测试用例,直到获得绿色状态。

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.