无论是编写简单应用程序还是更大的应用程序,我都使用
System.out
语句进行测试,这对我来说似乎非常容易。为什么要使用JUnit创建测试类,在项目中创建不必要的文件夹,如果我们仍然需要调用相同的方法,检查它们返回什么,那么我们就有了注释一切的额外负担?
为什么不编写一个类并使用
System.out
一次性进行测试,而不创建测试类呢?附注:我从未参与过大型项目,我只是在学习。
那么它的目的是什么?
System.out
语句进行测试,这对我来说似乎非常容易。System.out
一次性进行测试,而不创建测试类呢?现在首先,你对代码是否正确工作感兴趣是很好的。这是一件好事。你比别人更先进!不幸的是,这种方法存在问题。视觉检查有效,我检查代码是否按照预期运行,在这些情况下,一旦我确认它是正确的,我们就可以继续进行。
对于新的眼睛来说,仅仅因为他们没有与原始编码者的大脑合作,所以要验证代码的正确性是很困难的。第二双眼睛的所有者必须与代码的原始作者进行交流,才能充分理解有问题的代码。作为共享知识的手段,谈话是出了名的不可靠。如果原始编码者无法接触到新的眼睛,那么这一点就毫无意义了。在这种情况下,新的眼睛必须阅读原始代码。
阅读没有单元测试覆盖的其他人的代码比阅读有关联的单元测试的代码更加困难。最好的情况是阅读别人的代码是棘手的工作,而在软件工程中,这是最艰巨的任务。雇主在招聘职位时强调项目是全新的原因是有道理的。从头开始编写代码比修改现有代码更容易,因此使广告招聘更具吸引力。
通过单元测试,我们将代码分解成其组成部分。对于每个组件,我们都会陈述程序应该如何行动。每个单元测试都讲述了程序的一部分在特定情况下应该如何运作的故事。每个单元测试就像合同中的条款,描述了客户端代码视角下应该发生什么。最后,提供测试可以让您积极地重构代码,因为您可以在确信更改不会破坏现有测试的情况下进行大规模的代码改进。
测试驱动开发有一个警告,那就是您必须编写可测试的代码。这涉及到编写接口并使用诸如依赖注入之类的技术来实例化协作对象。请查看Kent Beck的工作,他很好地描述了TDD。查找编写接口并学习设计模式。
当你使用像System.out这样的东西进行测试时,你只能测试一小部分可能的用例。当你处理可能接受无限数量不同输入的系统时,这并不是非常彻底的。
单元测试旨在允许您使用非常大而且多样化的不同数据输入集来快速运行应用程序测试。此外,最好的单元测试还考虑边界情况,例如恰好位于有效范围边缘的数据输入。
对于一个人来测试所有这些不同的输入可能需要几周的时间,而机器只需要几分钟。
可以这样想:您也没有在“测试”静态内容。您的应用程序很可能会不断变化。因此,这些单元测试旨在在编译或部署周期的不同时间点运行。也许最大的优点是:
如果您在代码中出现问题,您将立即知道,而不是在部署后,也不是当QA测试人员发现错误时,也不是当您的客户取消时。您也有更好的机会立即修复故障,因为很明显导致代码问题的原因很可能是自上次编译以来发生的。因此,需要解决问题的调查工作量大大降低。
我添加了其他System.out无法做到的一些事情:
使每个测试用例独立(这很重要)
JUnit可以做到:每次都会创建新的测试用例实例并调用@Before
方法。
将测试代码与源代码分开
JUnit可以做到。
与CI集成
JUnit可以通过Ant和Maven来实现。
轻松安排和组合测试用例
JUnit可以使用@Ignore
和测试套件来实现。
轻松检查结果
JUnit提供了许多Assert方法(assertEquals
,assertSame
等)
Mock和Stub让您专注于测试模块
JUnit可以做到:使用Mock和Stub让您设置正确的夹具,并专注于测试模块逻辑。
单元测试确保代码按照预期工作。如果您需要更改代码以构建新功能或修复错误,它们还非常有用,以确保代码仍然按照预期工作。高测试覆盖率可以让您在不进行大量手动测试的情况下继续开发功能。
您使用的System.out
的手动方法是好的,但并不是最好的方法。这是您执行的一次性测试。在现实世界中,需求经常发生变化,大多数时候您会对现有函数和类进行大量修改。因此...您不必每次都测试已编写的代码。
JUnit 还具有一些更高级的特性,例如:
JUnit 提供了用于测试某些条件的方法,这些方法通常以 assert 开头,并允许您指定错误消息、期望结果和实际结果。
其中一些方法包括:
fail([message])
- 让测试失败。可以用来检查代码的某个部分是否被执行。或者在测试代码实现之前使用失败的测试。assertTrue(true)
/ assertTrue(false)
- 始终为true / false。如果测试尚未实现,可以预定义测试结果。assertTrue([message,] condition)
- 检查布尔值condition
是否为真。assertEquals([message,] expected, actual)
- 测试两个值是否相等(根据实现的equals
方法,否则使用==
引用比较)。注意:对于数组,检查的是引用而不是内容,请使用assertArrayEquals([message,] expected, actual)
。assertEquals([message,] expected, actual, delta)
- 测试两个浮点数或双精度值是否相距一定距离,由delta
值控制。assertNull([message,] object)
- 检查对象是否为null等等,这里 这里查看完整的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 { }
如果你不使用测试框架,就无法编写任何测试用例,否则你将不得不编写自己的测试框架来使你的测试用例更具说服力。以下是一些关于JUnit框架的信息,除此之外,您还可以使用TestNG框架。
Junit是Java编程语言中广泛使用的测试框架。您可以使用此自动化框架进行单元测试和UI测试。它帮助我们使用不同的注释定义代码的执行流程。Junit建立在“先测试,后编码”的理念上,这有助于提高测试用例的生产率和代码的稳定性。
Junit测试的重要特点:
JUNIT是Java开发人员通常接受的方法。 他们可以提供类似的预期输入给函数,并根据情况决定编写的代码是否完美,如果测试用例失败,则可能需要实现不同的方法。 JUNIT将加快开发速度,并确保函数中没有缺陷。