基于JUnit4和JUnit5配合例子讲解JUnit的两种运行方式

1 引言

最近读的书有老有新,在读的过程中都完全完成了相应例子的构建和运行。在读《Spring in Action》[1](#1)第4版时,其第37页的例子(以下称例子1)基于JUnit 4,并需要spring-test.jar;而在读《JUnit in Action》[2](#2)第3版时,其第7页的例子(以下称例子2)基于JUnit 5,并采用自动化构建工具Maven运行。为此作为学习记录,本文将自己手工运行例子1和Maven运行例子2的过程记录下来,给出手工和Maven运行JUnit的两种方式,同时比较JUnit 4 和 JUnit 5的些许区别。

JUnit 是一个单元测试框架,由软件工程领域的大牛Erich Gamma和Kent Beck在1997年发布。JUnit能帮助我们构建自动化单元测试,是Java语言自动化测试的事实标准。它能简化编写自动化测试程序,尤其是当程序规模大且不断变动时,亦即当进行回归测试时,其作用会凸显。

JUnit 4大概在2010年左右广泛使用,书《JUnit in Action》的第2版是基于此版本的。JUnit 5大概在2020年左右逐渐广泛使用,《JUnit in Action》[2](#2)第3版是基于此版本的。目前,在读一些稍微旧一点的书籍时,或者在参加一些项目的培训时,仍然会见到JUnit 4版本的使用。为此,本文例子1将基于JUnit 4讲解,而例子2将基于JUnit 5版本讲解。

例子1所依赖的环境为:

名称 版本
JDK 1.8.0_281
Spring 4.1.1
JUnit 4.11

例子2所依赖的环境为:

名称 版本
JDK 1.8.0_281
JUnit 5.6.0
Maven 3.8.8

2 例子

2.1 例子1

例子1本来在书中是利用Gradle工具构建的,我改成了手工运行,相应地文件夹结构也改成我自己的了。因为后续我还要运行该书中的很多例子,所以我把例子所依赖的jar都放到了一个固定的文件夹lib中,即D:\StudyCase\java\servletjsp\rbprj\springinaction\lib中。该例子是一个基于Spring的普通Java程序,并用xml配置文件形式写了测试程序,演示了Spring依赖注入的效果。

CompactDisc.java的内容如下:

java 复制代码
package soundsystem;

// javac -d classes src/soundsystem/CompactDisc.java

public interface CompactDisc {
  void play();
}

SgtPeppers.java的内容如下:

java 复制代码
package soundsystem;
import org.springframework.stereotype.Component;

// javac -classpath D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-core-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-beans-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-context-4.1.1.RELEASE.jar;classes;. -d classes src/soundsystem/SgtPeppers.java

@Component
public class SgtPeppers implements CompactDisc {

  private String title = "Sgt. Pepper's Lonely Hearts Club Band";  
  private String artist = "The Beatles";
  
  public void play() {
    System.out.println("Playing " + title + " by " + artist);
  }
  
}

采用自动配置方式的xml文件soundsystem.xml如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:context="http://www.springframework.org/schema/context"
  xmlns:c="http://www.springframework.org/schema/c"
  xmlns:p="http://www.springframework.org/schema/p"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

  <context:component-scan base-package="soundsystem" />

</beans>

测试代码CDPlayerXMLConfigTest.java的内容如下:

java 复制代码
package soundsystem;

// javac -classpath D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\system-rules-1.18.0.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\junit-4.11.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-test-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-core-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-beans-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-context-4.1.1.RELEASE.jar;classes;. -d classes src/soundsystem/CDPlayerXMLConfigTest.java

// java -classpath D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-aop-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\hamcrest-core-1.3.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\system-rules-1.18.0.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\junit-4.11.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-test-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-expression-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\commons-logging-1.1.3.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-core-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-beans-4.1.1.RELEASE.jar;D:\StudyCase\java\servletjsp\rbprj\springinaction\lib\spring-context-4.1.1.RELEASE.jar;classes;. org.junit.runner.JUnitCore soundsystem.CDPlayerXMLConfigTest

import static org.junit.Assert.*;

import org.junit.Rule;
import org.junit.Test;
//import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:soundsystem.xml")
public class CDPlayerXMLConfigTest {

  @Rule
  public final SystemOutRule log = new SystemOutRule().enableLog();

  //@Autowired
  //private MediaPlayer player;

  @Autowired
  private CompactDisc cd;

  @Test
  public void cdShouldNotBeNull() {
    assertNotNull(cd);
  }
  
  //@Test
  /*public void play() {
    player.play();
    assertEquals(
        "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\n", 
        log.getLog());
  }*/

}

从上面代码和前述中可以看出:

  1. Spring 4要与JUnit 4配合使用。
  2. 在JUnit 4 版本下,org.junit.contrib.java.lang.system.StandardOutputStreamLog已经不存在,需改用org.junit.contrib.java.lang.system.SystemOutRule,相应地在lib文件夹中需加上system-rules-1.18.0.jar,该jar的版本可微调。可见,虽然该书是2015年出版,JUnit 4也相对比较老了,但代码还是更老。
  3. 在JUnit 4 下,已经开始使用标注(Annotation)Test,且测试类不用继承JUnit 框架中的特定类。
  4. 由于是测试的基于Spring 4 的程序,需要包含spring-test-4.1.1.RELEASE.jar。

该例子经过编译后,即可运行,编译或运行指令已写在上面代码中。运行结果如下:

阅读上面的运行结果,可以看出运行成功,测试通过。虽然出现了3个 Could not instantiate TestExecutionListener,但是这3个TestExecutionListener与本程序无关。最关键的是

信息: Using TestExecutionListeners: org.springframework.test.context.support.DependencyInjectionTestExecutionListener@ba4d54, org.springframework.test.context.support.DirtiesContextTestExecutionListener@12bc6874

表明使用的是依赖注入TestExecutionListener,本例子正是测试依赖注入的。

手工不能直接运行该测试类,需要将测试类作为org.junit.runner.JUnitCore的参数运行,该类作为命令行的参数需用包的形式书写,即soundsystem.CDPlayerXMLConfigTest

2.2 例子2

该例子是一个非常简单的基于JUnit 5的例子。因为使用Maven运行,其文件夹结构需符合Maven的要求,如下:

bash 复制代码
├── src/
│   ├── main/
│   │	├──java/
│   │	│   ├──/com/manning/junitbook/ch01/Calculator.java
│   └── test/
│   │	├──java/
│   │	│   ├──/com/manning/junitbook/ch01/CalculatorTest.java
├── pom.xml

Calculator.java的内容如下:

java 复制代码
package com.manning.junitbook.ch01;

public class Calculator {
    public double add(double number1, double number2) {
        return number1 + number2;
    }
}

CalculatorTest.java的内容如下:

java 复制代码
package com.manning.junitbook.ch01;

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class CalculatorTest {

    @Test
    public void testAdd() {
        Calculator calculator = new Calculator();
        double result = calculator.add(10, 50);
        assertEquals(60, result, 0);
    }
}

pom.xml的内容如下:

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.manning.junitbook</groupId>
    <artifactId>ch01-jumpstart</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>ch01-jumpstart</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.6.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.6.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.6.0</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

在命令行中输入mvn clean install,会出现如下运行结果:

证明测试成功,并通过。同时会在根路径,即包含pom.xml的路径,产生target文件夹,其中内容包含可执行的class文件。使用Maven自动化构建工具是很方便的。

3 总结

本文基于不同的JUnit版本(4和5)分别介绍了JUnit的两种不同使用方式---手工和Maven,通过代码的方式可以看出JUnit 5的包结构发生了改变,能帮助初学者入门JUnit 框架的使用和代码阅读。若想对JUnit 更深入了解,推荐阅读文献[2](#2)


  1. Craig Walls. Spring in Action. 4th Edition. Shelter Island, NY: Manning Publications, 2015, pp37-37. ↩︎

  2. CĂTĂLIN TUDOSE. JUnit in Action. 3rd Edition. Shelter Island, NY: Manning Publications, 2020, pp7-12. ↩︎ ↩︎ ↩︎

相关推荐
测试修炼手册1 小时前
[测试技术] TestNG 入门与实战:分组、数据驱动与并行执行
junit·单元测试·log4j
our_times1 小时前
2026年Java开发者破局指南:Spring AI 2.0 与 Agent 开发实战
java·人工智能·spring
wear工程师2 小时前
@Transactional 标了却没开事务?先看这次调用有没有经过代理
java·spring
START_GAME3 小时前
LuaTools的使用指南
junit
飞雪金灵4 小时前
mac电脑 Maven下载安装和配置环境变量
macos·maven·maven下载安装·mac电脑环境
静水楼台x5 小时前
spring security
java·数据库·spring
咖啡八杯14 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
糖果店的幽灵14 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
Suhan4221 小时前
IDEA 设置启动参数和环境变量
spring boot·spring·编辑器·idea
测试修炼手册1 天前
[测试技术] JUnit 6 入门与实战:断言、参数化与 Mockito
数据库·junit·sqlserver