JUnit5学习之一:基本操作

Stella981
• 阅读 590

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

关于《JUnit5学习》系列

《JUnit5学习》系列旨在通过实战提升SpringBoot环境下的单元测试技能,一共八篇文章,链接如下:

  1. 基本操作
  2. Assumptions类
  3. Assertions类
  4. 按条件执行
  5. 标签(Tag)和自定义注解
  6. 参数化测试(Parameterized Tests)基础
  7. 参数化测试(Parameterized Tests)进阶
  8. 综合进阶(终篇)

本篇概览

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

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

关于JUnit5

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

SpringBoot对JUnit5的依赖

  1. 这里使用SpringBoot版本为2.3.4.RELEASE,在项目的pom.xml中依赖JUnit5的方法如下:

    org.springframework.boot spring-boot-starter-test test
  2. 如下图红框,可见JUnit5的jar都被spring-boot-starter-test间接依赖进来了:
    JUnit5学习之一:基本操作

曾经的RunWith注解

  1. 在使用JUnit4的时候,咱们经常这么写单元测试类:

    @RunWith(SpringRunner.class) @SpringBootTest public class XXXTest {

  2. 对于上面的RunWith注解,JUnit5官方文档的说法如下图红框所示,已经被ExtendWith取代:
    JUnit5学习之一:基本操作

  3. 咱们再来看看SpringBootTest注解,如下图,可见已经包含了ExtendWith:
    JUnit5学习之一:基本操作

  4. 综上所述,SpringBoot+JUnit5时,RunWith注解已经不需要了,正常情况下仅SpringBootTest注解即可,如果对扩展性有更多需求,可以添加ExtendWith注解,如下图:
    JUnit5学习之一:基本操作

常用的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之前的版本使用的,现在已经被废弃:

被废弃的注解

新的继任者

Before

BeforeEach

After

AfterEach

BeforeClass

BeforeAll

AfterClass

AfterAll

Category

Tag

RunWith

ExtendWith

Rule

ExtendWith

ClassRule

RegisterExtension

版本和环境信息

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

  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插件;

源码下载

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

名称

链接

备注

项目主页

https://github.com/zq2599/blog\_demos

该项目在GitHub上的主页

git仓库地址(https)

https://github.com/zq2599/blog\_demos.git

该项目源码的仓库地址,https协议

git仓库地址(ssh)

git@github.com:zq2599/blog_demos.git

该项目源码的仓库地址,ssh协议

  1. 这个git项目中有多个文件夹,本章的应用在junitpractice文件夹下,如下图红框所示:
    JUnit5学习之一:基本操作

  2. junitpractice是父子结构的工程,本篇的代码在junit5experience子工程中,如下图:
    JUnit5学习之一:基本操作

创建Maven父工程

  1. 为了便于管理整个系列的源码,在此建立名为junitpractice的maven工程,后续所有实战的源码都作为junitpractice的子工程;

  2. junitpractice的pom.xml如下,可见是以SpringBoot的2.3.4.RELEASE版本作为其父工程:

    4.0.0 simplebean org.springframework.boot spring-boot-starter-parent 2.3.4.RELEASE com.bolingcavalry junitpractice 1.0-SNAPSHOT pom <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> org.projectlombok lombok 1.16.16

本篇的源码工程

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

  1. 创建名为junit5experience的子工程,pom.xml如下,注意单元测试要依赖spring-boot-starter-test:

    4.0.0 com.bolingcavalry junitpractice 1.0-SNAPSHOT ../pom.xml com.bolingcavalry junit5experience 0.0.1-SNAPSHOT junit5experience Demo project for simplebean in Spring Boot junit5 <java.version>1.8</java.version> org.springframework.boot spring-boot-starter-web org.projectlombok lombok org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-webflux test org.springframework.boot spring-boot-maven-plugin

  2. 写一些最简单的业务代码,首先是service层的接口HelloService.java:

    package com.bolingcavalry.junit5experience.service;

    public interface HelloService {

    String hello(String name);
    int increase(int value);
    /**
     * 该方法会等待1秒后返回true,这是在模拟一个耗时的远程调用
     * @return
     */
    boolean remoteRequest();
    

    }

  3. 上述接口对应的实现类如下,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;
    }
    

    }

  4. 添加一个简单的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);
    }
    

    }

  5. 启动类:

    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工程,接下来一起为该工程编写单元测试用例;

编写测试代码

  1. 在下图红框位置新增单元测试类:
    JUnit5学习之一:基本操作

  2. 测试类的内容如下,涵盖了刚才提到的常用注解,请注意每个方法的注释说明:

    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);
    }
    

    }

  3. 接下来执行测试用例试试,点击下图红框中的按钮:
    JUnit5学习之一:基本操作

  4. 如下图,在弹出的菜单中,点击红框位置:
    JUnit5学习之一:基本操作

  5. 执行结果如下,可见Displayname注解的值作为测试结果的方法名展示,超时的方法会被判定为测试不通过,Disable注解修饰的方法则被标记为跳过不执行:
    JUnit5学习之一:基本操作

  6. 在父工程junitpractice的pom.xml文件所在目录,执行mvn test命令,可以看到maven执行单元测试的效果:
    JUnit5学习之一:基本操作

  • 至此,咱们对SpringBoot环境下的JUnit5有了最基本的了解,接下来的章节会展开更多知识点和细节,对单元测试做更深入的学习。

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

微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界…

本文分享 CSDN - 程序员欣宸。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Kurento实战之一:KMS部署和体验
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
3年前
Kurento实战之二:快速部署和体验
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Stella981 Stella981
3年前
OpenFaaS实战之四:模板操作(template)
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
3年前
gRPC学习之二:GO的gRPC开发环境准备
欢迎访问我的GitHub这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog\_demos(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fzq2599%2Fblog_demos)
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这