百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

JUnit5学习之一:基本操作

yuyutoo 2025-01-14 18:41 7 浏览 0 评论

欢迎访问我的GitHub

https://github.com/zq2599/blog_demos

内容:所有原创文章分类和汇总,及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;

关于《JUnit5学习》系列

《JUnit5学习》系列旨在通过一系列知识归纳和实战,具备在SpringBoot环境下开发和执行单元测试的能力;

本篇概览

本文是《JUnit5学习》系列的第一篇,通过实战学习在SpringBoot框架下JUnit5的基本功能,全篇章节如下:

  1. JUnit5简介
  2. SpringBoot对Junit5的依赖
  3. 常用注解简介
  4. 5版本已废弃的注解
  5. 进入实战环节,先介绍版本和环境信息
  6. 创建《JUnit5学习》系列源码的父工程
  7. 创建子工程,编码体验常用注解

关于JUnit5

  • JUnit是常用的java单元测试框架,5是当前最新版本,其整体架构如下(图片来自网络):
  • 从上图可见,整个Junit5可以划分成三层:顶层框架(Framework)、中间的引擎(Engine),底层的平台(Platform);
  • 官方定义Junit5由三部分组成:Platform、Jupiter、Vintage,功能如下;
  • Platform:位于架构的最底层,是JVM上执行单元测试的基础平台,还对接了各种IDE(例如IDEA、eclipse),并且还与引擎层对接,定义了引擎层对接的API;
  • Jupiter:位于引擎层,支持5版本的编程模型、扩展模型;
  • Vintage:位于引擎层,用于执行低版本的测试用例;
  • 可见整个Junit Platform是开放的,通过引擎API各种测试框架都可以接入;

SpringBoot对Junit5的依赖

  • 这里使用SpringBoot版本为2.3.4.RELEASE,在项目的pom.xml中依赖Junit5的方法如下:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
  • 如下图红框,可见Junit5的jar都被spring-boot-starter-test间接依赖进来了:

曾经的RunWith注解

  • 在使用Junit4的时候,咱们经常这么写单元测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class XXXTest {
  • 对于上面的RunWith注解,Junit5官方文档的说法如下图红框所示,已经被ExtendWith取代:
  • 咱们再来看看SpringBootTest注解,如下图,可见已经包含了ExtendWith
  • 综上所述,SpringBoot+Junit5时,RunWith注解已经不需要了,正常情况下仅SpringBootTest注解即可,如果对扩展性有更多需求,可以添加ExtendWith注解,如下图:

常用的Junit5注解(SpringBoot环境)

注意,接下来提到的测试方法,是指当前class中所有被@Test、@RepeatedTest、@ParameterizedTest、@TestFactory修饰的方法;

  1. ExtendWith:这是用来取代旧版本中的RunWith注解,不过在SpringBoot环境如果没有特别要求无需额外配置,因为SpringBootTest中已经有了;
  2. Test:被该注解修饰的就是测试方法;
  3. BeforeAll:被该注解修饰的必须是静态方法,会在所有测试方法之前执行,会被子类继承,取代低版本的BeforeClass;
  4. AfterAll:被该注解修饰的必须是静态方法,会在所有测试方法执行之后才被执行,会被子类继承,取代低版本的AfterClass;
  5. BeforeEach:被该注解修饰的方法会在每个测试方法执行前被执行一次,会被子类继承,取代低版本的Before;
  6. AfterEach:被该注解修饰的方法会在每个测试方法执行后被执行一次,会被子类继承,取代低版本的Before;
  7. DisplayName:测试方法的展现名称,在测试框架中展示,支持emoji;
  8. Timeout:超时时长,被修饰的方法如果超时则会导致测试不通过;
  9. Disabled:不执行的测试方法;

5版本已废弃的注解

以下的注解都是在5之前的版本使用的,现在已经被废弃:

版本和环境信息

整个系列的编码和执行在以下环境进行,供您参考:

  1. 硬件配置:处理器i5-8400,内存32G,硬盘128G SSD + 500G HDD
  2. 操作系统:Windows10家庭中文版
  3. IDEA:2020.2.2 (Ultimate Edition)
  4. JDK:1.8.0_181
  5. SpringBoot:2.3.4.RELEASE
  6. JUnit Jupiter:5.6.2
  • 接下来开始实战,咱们先建好SpringBoot项目;

关于lombok

为了简化代码,项目中使用了lombok,请您在IDEA中安装lombok插件;

源码下载

如果您不想编码,可以在GitHub下载所有源码,地址和链接信息如下表所示(
https://github.com/zq2599/blog_demos):

  • 这个git项目中有多个文件夹,本章的应用在junitpractice文件夹下,如下图红框所示:
  • junitpractice是父子结构的工程,本篇的代码在junit5experience子工程中,如下图:

创建Maven父工程

  • 为了便于管理整个系列的源码,在此建立名为junitpractice的maven工程,后续所有实战的源码都作为junitpractice的子工程;
  • junitpractice的pom.xml如下,可见是以SpringBoot的2.3.4.RELEASE版本作为其父工程:
<?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>
    <modules>
        <module>simplebean</module>
        <!--
        <module>testenvironment</module>
        -->
    </modules>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>junitpractice</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.16</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

本篇的源码工程

接下来咱们准备一个简单的SpringBoot工程用于做单元测试,该工程有service和controller层,包含一些简单的接口和类;

  • 创建名为junit5experience的子工程,pom.xml如下,注意单元测试要依赖spring-boot-starter-test:
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.bolingcavalry</groupId>
        <artifactId>junitpractice</artifactId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <groupId>com.bolingcavalry</groupId>
    <artifactId>junit5experience</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>junit5experience</name>
    <description>Demo project for simplebean in Spring Boot junit5</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
  • 写一些最简单的业务代码,首先是service层的接口HelloService.java:
package com.bolingcavalry.junit5experience.service;

public interface HelloService {
    String hello(String name);
    int increase(int value);
    /**
     * 该方法会等待1秒后返回true,这是在模拟一个耗时的远程调用
     * @return
     */
    boolean remoteRequest();
}
  • 上述接口对应的实现类如下,hello和increase方法分别返回String型和int型,remoteRequest故意sleep了1秒钟,用来测试Timeout注解的效果:
package com.bolingcavalry.junit5experience.service.impl;

import com.bolingcavalry.junit5experience.service.HelloService;
import org.springframework.stereotype.Service;

@Service()
public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(String name) {
        return "Hello " + name;
    }

    @Override
    public int increase(int value) {
        return value + 1;
    }

    @Override
    public boolean remoteRequest() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        }

        return true;
    }
}
  • 添加一个简单的controller:
package com.bolingcavalry.junit5experience.controller;

import com.bolingcavalry.junit5experience.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @Autowired
    private HelloService helloService;

    @RequestMapping(value = "/{name}", method = RequestMethod.GET)
    public String hello(@PathVariable String name){
        return helloService.hello(name);
    }
}
  • 启动类:
package com.bolingcavalry.junit5experience;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Junit5ExperienceApplication {

    public static void main(String[] args) {
        SpringApplication.run(Junit5ExperienceApplication.class, args);
    }
}
  • 以上就是一个典型的web工程,接下来一起为该工程编写单元测试用例;

编写测试代码

  • 在下图红框位置新增单元测试类:
  • 测试类的内容如下,涵盖了刚才提到的常用注解,请注意每个方法的注释说明:
package com.bolingcavalry.junit5experience.service.impl;

import com.bolingcavalry.junit5experience.service.HelloService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest
@Slf4j
class HelloServiceImplTest {

    private static final String NAME = "Tom";

    @Autowired
    HelloService helloService;

    /**
     * 在所有测试方法执行前被执行
     */
    @BeforeAll
    static void beforeAll() {
        log.info("execute beforeAll");
    }

    /**
     * 在所有测试方法执行后被执行
     */
    @AfterAll
    static void afterAll() {
        log.info("execute afterAll");
    }

    /**
     * 每个测试方法执行前都会执行一次
     */
    @BeforeEach
    void beforeEach() {
        log.info("execute beforeEach");
    }

    /**
     * 每个测试方法执行后都会执行一次
     */
    @AfterEach
    void afterEach() {
        log.info("execute afterEach");
    }

    @Test
    @DisplayName("测试service层的hello方法")
    void hello() {
        log.info("execute hello");
        assertThat(helloService.hello(NAME)).isEqualTo("Hello " + NAME);
    }

    /**
     * DisplayName中带有emoji,在测试框架中能够展示
     */
    @Test
    @DisplayName("测试service层的increase方法\uD83D\uDE31")
    void increase() {
        log.info("execute increase");
        assertThat(helloService.increase(1)).isEqualByComparingTo(2);
    }

    /**
     * 不会被执行的测试方法
     */
    @Test
    @Disabled
    void neverExecute() {
        log.info("execute neverExecute");
    }

    /**
     * 调用一个耗时1秒的方法,用Timeout设置超时时间是500毫秒,
     * 因此该用例会测试失败
     */
    @Test
    @Timeout(unit = TimeUnit.MILLISECONDS, value = 500)
    @Disabled
    void remoteRequest() {
        assertThat(helloService.remoteRequest()).isEqualTo(true);
    }
}
  • 接下来执行测试用例试试,点击下图红框中的按钮:
  • 如下图,在弹出的菜单中,点击红框位置:
  • 执行结果如下,可见Displayname注解的值作为测试结果的方法名展示,超时的方法会被判定为测试不通过Disable注解修饰的方法则被标记为跳过不执行
  • 在父工程junitpractice的pom.xml文件所在目录,执行mvn test命令,可以看到maven执行单元测试的效果:
  • 至此,咱们对SpringBoot环境下的Junit5有了最基本的了解,接下来的章节会展开更多知识点和细节,对单元测试做更深入的学习。

欢迎关注我的公众号:程序员欣宸

相关推荐

建筑福利-pdf转dwg格式转换器,再也不用描图-极客青年

作为一名经常熬夜画图的建筑狗或者cad用户,你体验过pdf图纸描图到cad吗?前几天一个老同学找我,说他的毕业设计需要我帮忙,发给我一份pdf图纸文件,问我怎么把pdf图纸转换成dwg格式。机智的我灵...

想学 HTML,不知从何入手?看完这篇文章你就知道了

很多人都说HTML是一门很简单的语言,看看书,看看视频就能读懂。但是,如果你完全没有接触过,就想通过看一遍教程,背背标签,想要完全了解HTML,真的有点太天真了。HTML中文...

「前端」HTML之结构

今天继续为大家分享前端的知识,如果对前端比较感兴趣的小伙伴,可以关注我,我会更大家继续分享更多与前端相关的内容,当然如果内容中又不当的或者文字错误的,欢迎大家在评论区留言,我会及时修改纠正。1.初识H...

手把手教你使用Python网络爬虫下载一本小说(附源码)

大家好,我是Python进阶者。前言前几天【磐奚鸟】大佬在群里分享了一个抓取小说的代码,感觉还是蛮不错的,这里分享给大家学习。...

用于处理pdf文件格式的转换器

在上传过程中如果单个文件太大则容易中断,而且文件太大的话对与存储也有些弊端。那么我们应该想到将文件进行压缩(注意这里压缩指的是不改变文件格式的压缩,而不是用变成压缩文件。这里就将以下用专门的软件压缩P...

乐书:在线 Kindle 电子书制作和转换工具

之前Kindle伴侣曾推荐过可以在Windows和Mac系统平台上运行的kindle电子书制作软件Sigil(教程),用它可以制作出高质量的的ePub格式电子书,当然最后还需要通...

付费文档怎么下载?教你5种方法,任意下载全网资源

网上查资料的时候,经常遇到需要注册登录或者付费的才能复制或者是下载,遇到这种情况大多数人都会选择重新查。...

捡来的知识!3种方法随便复制网页内容,白嫖真香呀

网上的资源真的多,所以许多人常常会从网上找资料。我们看到感兴趣的内容,第一时间可能会想要收入囊中。比如说截个图啊,或者挑选有意思的句子复制粘贴,记录下来。可是,有些时候,却会遇到这样的情况:1、内容不...

AI的使用,生成HTML网页。

利用deepseek,豆包,kimi以及通义千问,写入相同的需求。【写一个网页,实现抽奖功能,点击“开始”,按键显示“停止”,姓名开始显示在屏幕上,人员包括:“张三”,“里斯”,“Bool”,“流水废...

pdf转换成jpg转换器 4.1 官方正式版

pdf转换成jpg工具软件简介pdf转换成jpg转换器是一款界面简洁,操作方便的pdf转换成jpg转换器。pdf转换成jpg转换器可以将PDF文档转换为JPG,BMP,GIF,PNG,TIF图片文件。...

办公必备的office转换成pdf转换器怎么用?

2016-02-2415:53:37南方报道网评论(我要点评)字体刚从校园走出社会,对于快节奏的办公环境,难免会觉得有些吃力。在起步阶段力求将手头上的事情按时完工不出错,但是渐渐的你会发现,别人只...

为什么PDF转Word大多要收费?

PDF转Word大多都要收费?并非主要是因为技术上的难度,而是基于多方面的商业和版权考虑的,下面给大家浅分析下原因:...

如何用python生成简单的html report报告

前提:用python写了一个简单的log分析,主要也就是查询一些key,value出来,后面也可以根据需求增加。查询出来后,为了好看,搞个html表格来显示。需要的组件:jinja2flask...

学用系列|如何搞定word批量替换修改和格式转换?这里一站搞定

想必不少朋友都会碰到批量修改word文档内容、压缩文档图片、文件格式转换等重复性文档处理工作的需要,今天胖胖老师就推荐给大家一个免费工具XCLWinKits,一站搞定你所有的需要。什么是XCLWinK...

这款PDF文档转换神器,能帮你解决PDF使用中的许多难点

不管是平时的学习还是工作,相信许多朋友都经常接触PDF文件。可以说,PDF文件在我们的日常办公学习过程中的重要性和Word文档一样重要。在之前的更新中,小编介绍了几款非常不错的PDF文档格式转换软件,...

取消回复欢迎 发表评论: