单元测试是 测试软件 的第一级,您可以在其中编写测试代码来执行要测试的代码中的特定功能。在大多数情况下,作为程序员,您有责任交付经过单元测试的代码。目的是检查软件单元(例如被测类的公共方法)是否按预期运行和/或返回预期数据。单元测试 不是 在生产系统上进行的,而是作为独立的单元进行的。如果被测单元具有外部依赖性,例如外部数据源或 Web 服务,则依赖性将替换为测试实现或使用测试框架创建的模拟对象。单元测试不是唯一的类型,它本身无法处理所有测试方面。其他类型的测试,例如集成和功能测试,在测试软件中有其自身的作用。
在本系列文章中,我们将重点介绍使用 JUnit 进行单元测试——JUnit 是最流行的 Java 代码测试框架之一。在这篇文章中,我们将从创建和执行基本单元测试开始,然后在后续文章中转向特定的单元测试方面。
核心 JUnit 框架包含在一个 JAR 文件中,您可以下载该文件,将类路径指向它,然后创建和运行测试。但在这篇文章中,我们将学习如何以真正的程序员的方式执行单元测试。我们将从 Maven 开始,然后转向 IntelliJ。
使用 Maven 进行单元测试
您可能听说过 Maven 被称为构建工具。但是,除了从源代码构建可部署工件的能力之外,Maven 还提供了许多用于管理软件开发生命周期的特性。单元测试就是这样一种功能,它作为测试阶段合并到 Maven 构建生命周期 中。
在不深入研究 Maven 的情况下,让我们开始使用 Maven 进行第一个 JUnit 测试。
- 下载并安装 Maven (如果尚未完成)。
- 打开命令提示符 (Windows) 或终端(*uix 或 Mac),浏览到工作目录以设置项目,然后执行以下命令。
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
前面的
archetype:generate
命令使用
maven-archetype-quickstart
模板创建一个基本的 Maven 项目,该项目包含一个
pom.xml
文件、一个
App.java
类和一个
AppTest.java
测试类,目录结构如下。
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
在上面的目录结构中, pom.xml 文件,也称为 Maven 配置文件,是 Maven 项目的核心。它是您定义项目配置的地方——特别是项目的依赖项。例如,由于我们的项目依赖于 JUnit,因此我们需要在 pom.xml 文件中将其声明为依赖项。尽管默认情况下 JUnit 依赖项已经存在,但我们将更新它以指向最新的 JUnit 版本。这就是我们最终的 pom.xml 文件的样子。
pom.xml:
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
现在我们已经设置了一个基本的 Java 类、一个测试类和 pom.xml 配置,我们可以运行单元测试了。
-
从工作目录执行
mvn test
命令。
此命令将运行 Maven 为我们生成的默认
AppTest
类,输出如下。
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
我们已经使用 Maven 执行了 JUnit 测试。该测试通过了,但几乎没有提供任何价值。接下来,我们将使用 IntelliJ IDE 编写和执行更全面的测试。
IntelliJ 中的单元测试
使用 IntelliJ,您可以轻松创建、运行和调试单元测试。在其他几个单元测试框架中,IntelliJ 提供了对 JUnit 的内置支持。在 IntelliJ 中,您可以通过单击创建 JUnit 测试类,并在测试类及其对应的目标类之间快速导航以调试测试错误。 IntelliJ 中一个非常有用的单元测试功能是代码覆盖率。使用此功能,您可以查看项目中单元测试覆盖的方法甚至代码行的确切百分比。
让我们将现有的 Maven 项目导入 IntelliJ 并进行一些单元测试。
将 Maven 项目导入 IntelliJ
如果您没有安装 IntelliJ,请从 官方网站 下载并安装免费的社区版或终极版的 30 天试用版。完成后,执行以下步骤:
- 打开 IntelliJ。
- 在 “欢迎使用 IntelliJ IDEA” 窗口中,单击 “导入项目” 。
- 在 “选择要导入的文件或目录” 对话框中,浏览到 Maven 项目的工作目录并选择 pom.xml 文件。
- 单击 确定 按钮。
- 在出现的 Import Project from Maven 对话框中,选中 Import Maven projects automatically 复选框以在每次 pom.xml 文件更改时同步 Maven 和 InteliiJ 项目之间的更改。
- 通过几个对话框单击 Next 按钮,接受默认设置,最后单击 Finish 。 IntelliJ 的 项目 窗口显示项目结构。
- 双击 项目 窗口中的 App 以在代码编辑器中将其打开。
-
用这段代码替换
App
类的默认代码。
应用程序.java:
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
在上面的代码中,我们在
App
类中编写了一个
concatAndConvertString()
方法,它接受两个
String
参数。该方法首先连接字符串并将结果转换为大写,然后再返回。
接下来我们将添加一个测试类来测试
concatAndConvertString()
方法。
添加测试类
让我们从头开始完成在 IntelliJ 中添加测试类的步骤。
- 从 项目 窗口中删除默认的 AppTest 类。
- 在 Project 窗口中,在 main 下创建一个名为 test 的 目录,我们将使用 test 目录将测试代码与应用程序代码分开。
- 右键单击 test 并选择 Mark Directory As→Test Sources Root 。
-
在
App
类打开的代码编辑器中,按
Shift+F10
并选择 Create New Test 。
- 在出现的 Create Test 对话框中,选择 jUnit4 单选按钮和对应于我们将测试的 concatAndConvertString() 方法的复选框。
-
单击
确定
按钮。 JUnit 使用用
@Test
注释修饰的testConcatAndConvertString()
方法创建 AppTest 类。此注释告诉 JUnit 将该方法作为测试用例运行。在测试方法中,我们将编写代码来测试 App 的concatAndConvertString()
方法。
AppTest.Java:
mvn archetype:generate -DgroupId=guru.springframework.unittest.quickstart -DartifactId=unittest -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
在上面示例的第 12 行中,我们调用了
assertEquals()
方法,这是几种 JUnit 断言方法之一。此重载方法检查两个
String
对象是否相等。如果不是,该方法将抛出一个
AssertionError
。在我们的示例中,我们通过将预期的字符串值 (
HELLOWORLD
) 作为第一个参数传递并将
concatAndConvertString()
方法返回的实际值作为第二个参数传递来调用
assertEquals()
方法。
运行单元测试
要运行测试,请从 IntelliJ 的“运行”菜单中选择 “运行 AppTest” 或按 Shift+F10 。 运行 窗口显示测试结果。绿色突出显示框表示测试已完成且没有任何失败。
要了解如何报告测试失败,请将
expectedValue
变量的值更改为
HelloWorld
并按
Shift+F10
。
“运行”
对话框显示红色进度条以指示测试失败以及比较失败消息。
在关闭 IntelliJ 之前将
expectedValue
变量恢复为其原始值。
概括
此时,如果您在想 为什么不直接使用 System.out.println() 进行单元测试呢? 那你就想错了。将用于调试的 System.out.println() 插入到代码中是不可取的,因为它需要在每次程序运行时手动扫描输出,以确保代码按预期进行。想象一下在具有成百上千行代码的企业应用程序中执行此操作。另一方面,单元测试从客户端的角度检查代码在运行时的行为。这可以更好地了解软件发布时可能发生的情况。
每当您编写代码时,您也应该编写单元测试。编写单元测试将捕获编程错误并提高代码质量。许多专业开发人员提倡进行测试驱动开发 (TDD),即在编写应用程序代码之前先编写单元测试。
无论哪种方式,如果您在编写应用程序代码之前或之后编写单元测试,单元测试都会成为检测代码的宝贵资产。随着代码库的增长,您可以根据需要重构事物,并且更有信心您的更改不会产生意想不到的后果(即,您更改了一件事,却不小心破坏了另一件事)。
在 我关于使用 JUnit 进行单元测试的教程系列的第 2 部分 中,我将更深入地了解 JUnit 断言、JUnit 注释和 JUnit 测试套件。
使用 Spring 框架进行单元测试
测试是使用 Spring 框架的 企业应用程序开发 过程中不可或缺的一部分。 Spring Framework 的体系结构适用于模块化代码和更容易的单元测试。 Spring通过对底层测试框架进行抽象的TestContext Framework提供测试支持,例如JUnit和TestNG。您可以通过将 SpringJUnit4ClassRunner.class 设置为 @RunWith 注释的值来使用它。这告诉 Spring 使用 TestContext 的测试运行器而不是 JUnit 的内置测试运行器。我 在这里 写了一篇关于使用 JUnit 测试 Spring 应用程序的更深入的文章。