Junit5单元测试

2023-11-10

    • 配置

    • Maven配置

我用的spring版本是2.2.2。

其实引入一个就行:

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

里面就会引入单元测试相关的一系列jar,包括junit4和jupiter(junit5)都都引入了。

比如

spring-boot-test、spring-boot-test-autoconfigure、spring-test,

还有

<dependency>

<groupId>org.junit.jupiter</groupId>

<artifactId>junit-jupiter</artifactId>

<version>5.5.2</version>

<scope>compile</scope>

</dependency>【里面包括jupiter一系列的jar包,可以看到junit是4,jupiter是5】

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-junit-jupiter</artifactId>

<version>3.1.0</version>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.assertj</groupId>

<artifactId>assertj-core</artifactId>

<version>3.13.2</version>

<scope>compile</scope>

</dependency>

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-core</artifactId>

<version>3.1.0</version>

<scope>compile</scope>

</dependency>【mockito-core-3.1.0.jar,核心mock相关都在里面

等等。

另外看使用情况可能需要再单独引入:

<dependency>

<groupId>org.junit.platform</groupId>

<artifactId>junit-platform-launcher</artifactId>

<version>1.5.2</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<scope>test</scope>

</dependency>【junit-4.12.jar】

<dependency>

<groupId>io.gitee.ForteScarlet</groupId>

<artifactId>mock.java</artifactId>

<version>1.7.1</version>

</dependency>

引入的jar是mock.java-1.7.1.jar(MockObject、Mock等)

    • yml配置

test文件夹下是有自己独立的resources的:

bootstrap.yml

application-junit.yml

有了这些配置才能在test类上面加上注解:

@ExtendWith(SpringExtension.class)

@SpringBootTest(classes = RrmApplication.class, properties = {"system.data-auth.enabled=true"})

这样跑test时就会启动spring并支持spring的注解。

当然也可以不加这两个注解,那就跑普通test不启动spring相关。

    • 引入内置mongoDB数据库服务以支持单元测试

在pom.xml中加入:

<dependency>

<groupId>com.spring4all</groupId>

<artifactId>mongodb-plus-spring-boot-starter</artifactId>

<version>1.0.0.RELEASE</version>

</dependency>

另外还要在test下的application-junit.yml加上:

spring:

data:

mongodb:

uri: mongodb://localhost:27017/fms_rrm

    • 引入内嵌redis服务支持单元测试

在pom.xml中加入:

<dependency>

<groupId>it.ozimov</groupId>

<artifactId>embedded-redis</artifactId>

<version>0.7.3</version>

<scope>test</scope>

<exclusions>

<exclusion>

<groupId>com.google.guava</groupId>

<artifactId>guava</artifactId>

</exclusion>

<exclusion>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-simple</artifactId>

</exclusion>

</exclusions>

</dependency>

在test下的application-junit.yml加上:

spring:

redis:

host: localhost

port: 6380

    • 注解

0.@Test 修饰在方法上

1.@DisplayName 说明,运行时会看到,修饰在类上和方法上都可以

2.@before 只跑一次

3. @beforeEach每个test方法前都会跑一次

@BeforeAll

@AfterEach

@AfterAll

一般:

@BeforeEach

void init() {mongodb构造数据等}

@AfterEach

void clear() {mongodb清理数据等}

4. JUnit 4到5迁移吗?从@RunWith到@ExtendWith的过渡(修饰在类上)

当涉及Spring时:

如果您想在测试中使用Spring测试框架功能(例如)@MockBean,则必须使用@ExtendWith(SpringExtension.class)。它取代了不推荐使用的JUnit4@RunWith(SpringJUnit4ClassRunner.class)

当不涉及Spring时:

例如,如果您只想涉及Mockito而不必涉及Spring,那么当您只想使用@Mock/ @InjectMocks批注时,可写成这样:@ExtendWith(MockitoExtension.class),因为它不会加载到很多不需要的Spring东西中。它替换了不推荐使用的JUnit4 @RunWith(MockitoJUnitRunner.class)。

5. @MockBean

@Mock

@InjectMocks

For1:

@InjectMocks

private SomeHandler someHandler;

@Mock

private OneDependency oneDependency; // 此mock将被注入到someHandler

InjectMocks,就好比是service层的对象。Mock好比是dao层对象。

当然也可以用代码的方式将私有属性注入对象中:

public static void setProperty(Object target, String name, Object value) throws NoSuchFieldException {

Field field = target.getClass().getDeclaredField(name);

ReflectionUtils.makeAccessible(field);

ReflectionUtils.setField(field, target, value);

}

6.@SpringBootTest(classes = RrmApplication.class, properties = {"system.data-auth.enabled=true"})

修饰在类上。

如果单元测试要启动spring相关,一个例子如下:

@ExtendWith({SpringExtension.class})

@SpringBootTest(classes = {RrmApplication.class})

@DisplayName("清除机器人位置坐标信息")

class ClearRobotLocationInfoTest {...}

7.@Ignore 修饰类,忽略该单元测试类(修饰方法貌似没用)

8.启用内置kafka: @EmbeddedKafka(topics = {"fms.rrm.clearPositionInfo"})

    • 测试方法

    • 规范

示例:

@Test

@DisplayName("查询数据服务日志审计-总条数为0")

void test_pageCallLog_countIsZero() {

PageCallLogReq req = buildPageCallLogReq(LocalDate.now(), LocalDate.now(), null);

Mockito.when(repository.countCallLog(req.getParams())).thenReturn(0L);

PageResp<List<PageCallLogDTO>> rest = dataServiceImpl.pageCallLog(req);

assertEquals(rest.getTotal(), 0L);

assertNull(rest.getData());

}

测试方法命名规范:test_[要测试的方法]_[要测试的分支情况]

    • 测试protected与private方法

一般是不建议单独针对protected与private方法去做单元测试,单元测试应该是只针对public方法,在public方法中调用的protected与private方法也会被同时测试到。

如果实在要测试protected与private方法,则要采用反射方式去调用。

For:

@InjectMocks

private IccidPosListener iccidPosListener = new IccidPosListener();

@Mock

private AsyncIccidPosDeal asyncIccidPosDeal;

@Test

@DisplayName("handleIccid方法正常执行")

void test_handleIccid_normal(){

try {

Method method = iccidPosListener.getClass().getDeclaredMethod("handleIccid", ConsumerRecord.class);

ConsumerRecord<String, String> record = new ConsumerRecord("topic", 1, 2, "key", "{'deviceCode': 'deviceCode2', 'iccid': 'iccid2'}");

Mockito.doNothing().when(asyncIccidPosDeal).handlePosRecord(Mockito.any(RobotIccidDto.class), Mockito.any(IccidUpdateResult.class));

method.setAccessible(true);

method.invoke(iccidPosListener, record);

Mockito.verify(asyncIccidPosDeal).handlePosRecord(Mockito.any(RobotIccidDto.class), Mockito.any(IccidUpdateResult.class));

} catch (Exception e) {

e.printStackTrace();

assertTrue(false);

}

}

    • 指定方法顺序

在类上加上注解:

@TestMethodOrder (MethodOrderer.Alphanumeric.class ) //字母数字自然顺序

然后单元测试方法命名如test1、test2.。。。

在运行该类单元测试时就会按方法命名顺序去执行各个方法。

当然还有其他方法指定顺序。

    • Mock

这个是单元测试的核心知识点。

    • mock对象

repository = Mockito.mock(DataServiceRepository.class);

或者直接用@InjectMocks 与 @Mock

    • mock行为(方法)

    • mock普通方法

1.方法有返回值:Mockito.when(repository.countCallLog(req.getParams())).thenReturn(12L);

还可以这样写:

Mockito.doReturn(future).when(kafkaTemplate).send(any(), any());

2.方法无返回值:

Mockito.doNothing().when(bIPMsgService.sendBip(Mockito.any()));

但在junit5这样写貌似会报错,改成如下:

Mockito.doNothing().when(repository).exportCallLog(req, Mockito.any(Consumer.class));

Ps:对象本身就是mock出来的,那这个doNothing语句可以不用写,因为mock出来的对象默认就是跳过其所有方法的。

3.如果无返回值的方法模拟抛出异常也要修改写法:

Mockito.when(fileConfigurationRepository.delete(GlobalConfiguration.CUSTOM_LOGO_ID)).thenThrow(new Exception());

改为:

Mockito.doThrow(new RuntimeException()).when(fileConfigurationRepository).delete(GlobalConfiguration.CUSTOM_LOGO_ID);

    • mock静态方法

Jdk8以前可以用PowerMockito来mock静态方法,但jdk8及以后就不能用了。

要用Mockito 3.4以上的版本就可直接支持。

所以需要额外引入:

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-core</artifactId>

<version>3.9.0</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-inline</artifactId>

<version>4.1.0</version>

<scope>test</scope>

</dependency>

其实如果springboot本身的版本在2.5以上的话mockito也在3.4以上了(会使得spring-boot-starter-test也在2.5以上,可以直接单独指定spring-boot-starter-test版本为2.5.3以上),这样就不用引入mockito-core,但mockito-inline还是要引入的。

1.对于无返回值void的静态方法:

try(MockedStatic<ExcelExport> mock = Mockito.mockStatic(ExcelExport.class)){

dataServiceImpl.exportCallLog(req, response);//里面会调用ExcelExport.export静态方法

}

注意要test的方法要写在try作用域里面。

因为这里已经mock了静态方法所在的类,默认就会跳过其下所有静态方法,所有不用再mock静态方法。

Ps: 目前问题,对于返回void的静态方法如何模拟抛出异常???

2.对于有返回值但入参为空的静态方法:

try(MockedStatic<ExcelExport> mock = Mockito.mockStatic(ExcelExport.class)){

mock.when(ExcelExport::export).thenReturn(1);

dataServiceImpl.exportCallLog(req, response);

}

3.对于有返回值且有入参为空的静态方法:

try(MockedStatic<ExcelExport> mock = Mockito.mockStatic(ExcelExport.class)){

mock.when(() -> ExcelExport.export(PoiExcelInfo.builder().build(), response)).thenReturn(1);

dataServiceImpl.exportCallLog(req, response);

}

这里面也有个要点:静态方法export的入参是一个PoiExcelInfo对象,我写成Mockito.any(PoiExcelInfo.class)是会报错的,随意new一个对象反而不会报错,也能正常跳过该静态方法。

    • mock私有方法

待补充

    • 模拟方法抛异常

对于无返回值的:

Mockito.doThrow(new RuntimeException("抛出异常")).when(asyncIccidPosDeal).handlePosRecord(Mockito.any(RobotIccidDto.class), Mockito.any(IccidUpdateResult.class));

貌似不能直接抛new Exception()的,需要指定一种具体的异常类,如RuntimeException。

对于有返回值的:

Mockito.when(robotLocationService.queryDeviceLocation(Mockito.anyString())).thenThrow(new RuntimeException("异常哈哈"));

ps:抛的指定异常必须是这个方法真的有throws这种异常,否则只能模拟抛出运行时异常,要不就会报错:

Checked exception is invalid for this method!

    • mock方法指定入参

·任意字符串:Mockito.anyString()

·任意指定对象:Mockito.any(UserDTO.class)

·如果入参是一个函数式接口,比如Consumer,好像不能用Mockito.any(Consumer.class),要试下用doc -> {} 是否可以,比如:

Mockito.doNothing().when(repository).exportCallLog(req, doc -> {});这个不确定,待定。

当然还有很多Mockito.anyInt()之类的,但非必要统一用Mockito.any()就好。

    • mock redis

如果是启动spring的单元测试,但又想跳过redis的相关操作,可以这样配置:

在test下面创建类:

@Configuration

public class JunitTestConfig{

@Bean

public RedisTemplate redisTemplate() {

RedisTemplate redisTemplate = Mockito.mock(RedisTemplate.class);

ValueOperations valueOperations = Mockito.mock(ValueOperations.class);

SetOperations setOperations = Mockito.mock(SetOperations.class);

HashOperations hashOperations = redisTemplate.opsForHash();

ListOperations listOperations = redisTemplate.opsForList();

ZSetOperations zSetOperations = redisTemplate.opsForZSet();

when(redisTemplate.opsForSet()).thenReturn(setOperations);

when(redisTemplate.opsForValue()).thenReturn(valueOperations);

when(redisTemplate.opsForHash()).thenReturn(hashOperations);

when(redisTemplate.opsForList()).thenReturn(listOperations);

when(redisTemplate.opsForZSet()).thenReturn(zSetOperations);

RedisOperations redisOperations = Mockito.mock(RedisOperations.class);

RedisConnection redisConnection = Mockito.mock(RedisConnection.class);

RedisConnectionFactory redisConnectionFactory = Mockito.mock(RedisConnectionFactory.class);

when(redisTemplate.getConnectionFactory()).thenReturn(redisConnectionFactory);

when(valueOperations.getOperations()).thenReturn(redisOperations);

when(redisTemplate.getConnectionFactory().getConnection()).thenReturn(redisConnection);

return redisTemplate;

}

}

    • 断言

单元测试的规范之一就是必须要写断言,而且不能写成assertTrue(true);这样绝对正确的断言。针对各种情况的断言这里一一介绍。

Assert是junit4的,而Assertions是junit5(jupiter5)的。

    • 测试的方法有返回值的

就断言方法返回值等于预期值就好,如:

assertEquals(rest.getTotal(), 12L);

assertNull(rest.getData());

Assertions.assertTrue(robots1.size() > 0);

    • 测试的方法返回void的

断言某对象有执行某方法:

对于返回void函数,测试时可以这样断言:(这个例子不对,verify要求robotExceptionReq对象也要是mock对象才行,所以找个mock对象就行啦!)

RobotExceptionReq robotExceptionReq = RobotExceptionReq.builder().build();

exceptionMessageHelper.handleMessage(robotExceptionReq);

Mockito.verify(robotExceptionReq).setCategory(Mockito.anyString());

证明robotExceptionReq执行了setCategory方法。

还可以写成断言某对象调用了某方法几次:

Mockito.verify(studentRepository, Mockito.times(2)).save(studentEntity);

    • 测试的方法抛异常的

For1:

Assertions.assertThrows(ServiceException.class,() -> controller.getExceptionCodeCount(req));

For2:

MyException thrown =

assertThrows(MyException.class,

() -> myObject.doThing(),

"Expected doThing() to throw, but it didn't");

assertTrue(thrown.getMessage().contains("Stuff"));

    • 在idea中使用单元测试

    • 创建junit单元测试类

对着类Ctrl + Shift + T

或者:

在需要测试的类或接口(推荐基于接口创建)名称上使用Alt+Enter,然后选择创建测试。

    • 查看覆盖率

Idea: Ctrl+Alt+F6 查看测试覆盖率

或者直接在类上面运行覆盖率:

执行后:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Junit5单元测试 的相关文章

随机推荐

  • Day 21 B. T-primes

    Problem We know that prime numbers are positive integers that have exactly two distinct positive divisors Similarly we l
  • Windows下安装nvm及搭建Angular环境的步骤整理

    环境 操作系统 windows 8 1 安装nvm nvm windows 下载https github com coreybutler nvm windows releases 我把nvm noinstall zip解压到c dev nv
  • PS2汉化1 字库处理

    引语 其实字库处理很难说有一个统一的方法 不同的程序都需要不同方法来处理 关于常见位图字库的详细信息 下面是字库存在于ELF ERX文件时的处理思路 字模替换 最天真朴素 最通用的处理方式 适用于原字库大小能够塞下汉化所用的全部文字的情况
  • XSS、CSRF攻击以及预防手段

    文章目录 XSS 反射型 持久型 DOM型 XSS如何防御 CSRF XSS XSS全程Cross Site Scripting 名为跨站脚本攻击 是一种常见于 Web 应用中的计算机安全漏洞 恶意攻击者往 Web 页面里嵌入恶意的客户端脚
  • 【VBA编程】VBA基础语法(一)

    一 VBA中的数据类型 VBA里的数据类型有 字节型 Byte 整数型 Integer 长整数型 Long 单精度浮点型 Single 双精度浮点型 Double 货币型 Currency 小数型 Decimal 字符串型 String 日
  • 《数据清洗》第五章操作实例

    案例一介绍 通过Kettle工具 消除CSV文件merge csv中完全重复的数据 1 打开Kettle工具 创建转换 通过使用Kettle工具 创建一个转换repeat transform 并添加 CSV文件输入 控件 唯一行 哈希值 控
  • 把多页Word文档缩小打印到同一张纸上

    方法一 首先要确定纸张的大小和方向 系统默认的是 A4 纸 方向纵向 如果要改变 可单击 文件 页面设置 进行相应的更改 单击 插入 文本框 横排 然后在刚才新建的文档中画出一个文本框 大小大约为这页纸的四分之一 然后将鼠标放到该文本框的边
  • 关于某些特殊时候按钮disabled属性失效时的解决办法

    今天遇到了一个BUG 导致下一步按钮中的属性即时有disabled时 点击按钮依然会触发按钮的点击事件 以下为下一步按钮的JS代码 下一步 btn step click function e if this hasClass layui b
  • 常见排序算法--合并排序

    思路 将一个无序的序列分组 直至分为每两个元素一组 如果有单个元素剩余 则可以剩余的单个元素自己一组 小组内排序 然后合并成一个有序的序列 例子 排序过程如图所示 图片摘选自 https blog csdn net ZY cat artic
  • 腾讯云nginx配置ssl证书实现https

    1 申请证书 2 下载证书 签发后 里面包含nginx 1 1 域名 bundle crt 2 2 域名 key 3 配置nginx文件 在配置ssl证书之前 要确保你的nginx已经安装了ssl模块 如果没有请安装 gt sbin ngi
  • rstudio导入txt文件_r语言怎么读取txt文件

    展开全部 1 r语言62616964757a686964616fe59b9ee7ad9431333431376533读取txt文件的方法 首先根据下图图片中的命令代码进行输入 2 然后这样就可以读取txt文件了 结果图如下 3 R读取csv
  • 模拟网盘(基于多线程TCP)

    题目描述 模拟一个网盘实现以下功能 用户可以注册 登录 上传和下载文件 用户可以实现类似 ls的功能查询文件信息 多用户间文件可以共享 要求 基于TCP协议 server端要用多线程实现 在linux下用C语言实现 需要实现的功能 服务器端
  • 基于java的支付宝即时到账支付

    java实现支付宝即时到账支付 演示地址 支付宝电脑网站支付 支持回调 支付成功后即可回到自己自定义的回调页面 下载地址 java实现支付宝即时到账支付 源码世界 下载后在本地只需把你的几个配置参数改成自己的即可完美实现在线支付 应用ID
  • Java封装的概念

    封装是面向对象的三大特征之一 封装 继承 多态 概念 封装就是将类里的某些信息隐藏 不允许外部程序直接调用 可以对成员变量更准确的控制 举例 通过以上代码 可以看到 如过X类的成员变量直接被调用 那么可能会出现赋值越界的情况 年龄不可能小于
  • MongoDB分片实战(一):集群搭建

    随笔 97 文章 9 评论 337 MongoDB分片实战 一 集群搭建 环境准备 Linux环境 主机 OS 备注 192 168 32 13 CentOS6 3 64位 普通PC 192 168 71 43 CentOS6 2 64位
  • Maven 4、JDK配置

    当你的idea中有多个jdk的时候 就需要指定你编译和运行的jdk 在settings xml中配置
  • 使用命令把类打成jar包

    测试用类 public class Hello public static void main String args System out println hello world 一般的Jar包 生成class文件 在命令行中输入下面代码
  • windows桌面应用自动化测试

    1 AutoIt3 原理 使用spy抓应用的hwnd 根据hwnd获取窗口信息 模拟发送鼠标按键 移动窗口实现自动化操作 缺点 获取到的信息少 编程实现复杂 2 UIAutomation msdn介绍 Microsoft UI Automa
  • STM32 USB DFU功能

    STM32 USB DFU功能 工具的安装配置 CubeMX上配置 完善接口 工具使用 HEX固件转为DFU文件 更新固件 DFU特点 工程代码 DFU的全称为 DownLoad Firmware Update即固件升级 以下配置以STM3
  • Junit5单元测试

    配置 Maven配置 我用的spring版本是2 2 2 其实引入一个就行