共计 5871 个字符,预计需要花费 15 分钟才能阅读完成。
欢送拜访我的 GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,波及 Java、Docker、Kubernetes、DevOPS 等;
对于《JUnit5 学习》系列
《JUnit5 学习》系列旨在通过实战晋升 SpringBoot 环境下的单元测试技能,一共八篇文章,链接如下:
- 基本操作
- Assumptions 类
- Assertions 类
- 按条件执行
- 标签 (Tag) 和自定义注解
- 参数化测试 (Parameterized Tests) 根底
- 参数化测试 (Parameterized Tests) 进阶
- 综合进阶(终篇)
本篇概览
本文是《JUnit5 学习》系列的第三篇,次要是学习 Assertions 类(org.junit.jupiter.api.Assertions),Assertions 类的一系列静态方法给咱们提供了单元测试时罕用的断言性能,本篇次要内容如下:
- Assertions 源码剖析
- 写一段代码,应用 Assertions 的罕用静态方法
- 应用异样断言
- 应用超时断言
- 理解第三方断言库
源码下载
- 如果您不想编码,能够在 GitHub 下载所有源码,地址和链接信息如下表所示:
名称 | 链接 | 备注 |
---|---|---|
我的项目主页 | https://github.com/zq2599/blo… | 该我的项目在 GitHub 上的主页 |
git 仓库地址(https) | https://github.com/zq2599/blo… | 该我的项目源码的仓库地址,https 协定 |
git 仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该我的项目源码的仓库地址,ssh 协定 |
- 这个 git 我的项目中有多个文件夹,本章的利用在 <font color=”blue”>junitpractice</font> 文件夹下,如下图红框所示:
- <font color=”blue”>junitpractice</font> 是父子构造的工程,本篇的代码在 <font color=”red”>assertassume</font> 子工程中,如下图:
Assertions 源码剖析
- 下图是一段最简略最常见的单元测试代码,也就是 Assertions.assertEquals 办法,及其执行成果:
- 将 Assertions.assertEquals 办法逐层开展,如下图所示,可见入参 expected 和 actual 的值如果不相等,就会在 AssertionUtils.fail 办法中抛出 AssertionFailedError 异样:
- 用类图工具查看 <font color=”blue”>Assertions</font> 类的办法,如下图,大部分是与 assertEquals 办法相似的判断,例如对象是否为空,数组是否相等,判断失败都会抛出 AssertionFailedError 异样:
- 判断两个数组是否相等的逻辑与判断两个对象略有不同,能够重点看看,办法源码如下:
public static void assertArrayEquals(Object[] expected, Object[] actual) {AssertArrayEquals.assertArrayEquals(expected, actual);
}
- 将上述代码逐层开展,在 AssertArrayEquals.java 中见到了残缺的数组比拟逻辑,如下图:
- 接下来,咱们编写一些单元测试代码,把 Assertions 类罕用的办法都相熟一遍;
编码实战
- 关上 junitpractice 工程的子工程 assertassume,新建测试类 <font color=”blue”>AssertionsTest.java</font>:
- 最简略的判断,两个入参相等就不抛异样(AssertionFailedError):
@Test
@DisplayName("最一般的判断")
void standardTest() {assertEquals(2, Math.addExact(1, 1));
}
- 还有另一个 assertEquals 办法,能承受 <font color=”blue”>Supplier</font> 类型的入参,当判断不通过时才会调用 Supplier.get 办法获取字符串作为失败提醒音讯(如果测试通过则 Supplier.get 办法不会被执行):
@Test
@DisplayName("带失败提醒的判断(拼接音讯字符串的代码只有判断失败时才执行)")
void assertWithLazilyRetrievedMessage() {
int expected = 2;
int actual = 1;
assertEquals(expected,
actual,
// 这个 lambda 表达式,只有在 expected 和 actual 不相等时才执行
()->String.format("期望值[%d],理论值[%d]", expected, actual));
}
- assertAll 办法能够将多个判断逻辑放在一起解决,只有有一个报错就会导致整体测试不通过,并且执行后果中会给出具体的失败详情:
@Test
@DisplayName("批量判断(必须全副通过,否则就算失败)")
void groupedAssertions() {
// 将多个判断放在一起执行,只有全副通过才算通过,如果有未通过的,会有对应的提醒
assertAll("单个测试方法中多个判断",
() -> assertEquals(1, 1),
() -> assertEquals(2, 1),
() -> assertEquals(3, 1)
);
}
上述代码执行后果如下:
异样断言
- Assertions.assertThrows 办法,用来测试 Executable 实例执行 execute 办法时是否抛出指定类型的异样;
- 如果 execute 办法执行时不抛出异样,或者抛出的异样与冀望类型不统一,都会导致测试失败;
- 写段代码验证一下,如下,1 除以 0 会抛出 <font color=”blue”>ArithmeticException</font> 异样,合乎 assertThrows 指定的异样类型,因而测试能够通过:
@Test
@DisplayName("判断抛出的异样是否是指定类型")
void exceptionTesting() {
// assertThrows 的第二个参数是 Executable,// 其 execute 办法执行时,如果抛出了异样,并且异样的类型是 assertThrows 的第一个参数(这里是 ArithmeticException.class),// 那么测试就通过了,返回值是异样的实例
Exception exception = assertThrows(ArithmeticException.class, () -> Math.floorDiv(1,0));
log.info("assertThrows 通过后,返回的异样实例:{}", exception.getMessage());
}
- 以上是 Assertions 的惯例用法,接下来要重点关注的就是和超时相干的测试方法;
超时相干的测试
- 超时测试的次要指标是验证指定代码是否在规定工夫内执行完,最罕用的 <font color=”blue”>assertTimeout</font> 办法外部实现如下图,可见被测试的代码通过 <font color=”blue”>ThrowingSupplier</font> 实例传入,被执行后再查看耗时是否超过规定工夫,超过就调用 fail 办法抛 AssertionFailedError 异样:
- assertTimeout 的用法如下,冀望工夫是 1 秒,实际上 Executable 实例的 execute 用了两秒才实现,因而测试失败:
@Test
@DisplayName("在指定工夫内实现测试")
void timeoutExceeded() {
// 指定工夫是 1 秒,理论执行用了 2 秒
assertTimeout(ofSeconds(1), () -> {
try{Thread.sleep(2000);
} catch (InterruptedException e) {e.printStackTrace();
}
});
}
执行后果如下图:
- 下面的演示中,assertTimeout 的第二个入参类型是 <font color=”blue”>Executable</font>,此外还有另一个 assertTimeout 办法,其第二个入参是 <font color=”blue”>ThrowingSupplier</font> 类型,该类型入参的 get 办法必须要有返回值,假如是 XXX,而 assertTimeout 就拿这个 XXX 作为它本人的返回值,应用办法如下:
@Test
@DisplayName("在指定工夫内实现测试")
void timeoutNotExceededWithResult() {
// 筹备 ThrowingSupplier 类型的实例,// 外面的 get 办法 sleep 了 1 秒钟,而后返回一个字符串
ThrowingSupplier<String> supplier = () -> {
try{Thread.sleep(1000);
} catch (InterruptedException e) {e.printStackTrace();
}
return "我是 ThrowingSupplier 的 get 办法的返回值";
};
// 指定工夫是 2 秒,实际上 ThrowingSupplier 的 get 办法只用了 1 秒
String actualResult = assertTimeout(ofSeconds(2), supplier);
log.info("assertTimeout 的返回值:{}", actualResult);
}
上述代码执行后果如下,测试通过并且 ThrowingSupplier 实例的 get 办法的返回值也被打印进去:
- 方才咱们看过了 assertTimeout 的外部实现代码,是将入参 Executable 的 execute 办法执行实现后,再查看 execute 办法的耗时是否超过预期,这种办法的弊病是必须期待 execute 办法执行实现才晓得是否超时,assertTimeoutPreemptively 办法也是用来检测代码执行是否超时的,然而防止了 assertTimeout 的必须期待 execute 执行实现的弊病,防止的办法是用一个新的线程来执行 execute 办法,上面是 assertTimeoutPreemptively 的源码:
public static void assertTimeoutPreemptively(Duration timeout, Executable executable) {AssertTimeout.assertTimeoutPreemptively(timeout, executable);
}
- assertTimeoutPreemptively 办法的 Executable 入参,其 execute 办法会在一个新的线程执行,假如是 XXX 线程,当等待时间超过入参 timeout 的值时,XXX 线程就会被中断,并且测试后果是失败,上面是 assertTimeoutPreemptively 的用法演示,设置的超时工夫是 2 秒,而 Executable 实例的 execute 却 sleep 了 10 秒:
@Test
void timeoutExceededWithPreemptiveTermination() {log.info("开始 timeoutExceededWithPreemptiveTermination");
assertTimeoutPreemptively(ofSeconds(2), () -> {log.info("开始 sleep");
try{Thread.sleep(10000);
log.info("sleep 了 10 秒");
} catch (InterruptedException e) {log.error("线程 sleep 被中断了", e);
}
});
}
- 来看看执行后果,如下图,通过日志可见,Executable 的 execute 办法是在新的线程执行的,并且被中断了,提前完成单元测试,测试后果是不通过:
第三方断言库
- 除了 junit 的 Assertions 类,还能够抉择第三方库提供的断言能力,比拟典型的有 AssertJ, Hamcrest, Truth 这三种,它们都有各自的特色和实用场景,例如 Hamcrest 的特点是匹配器(matchers),而 Truth 来自谷歌的 Guava 团队,编写的代码是链式调用格调,简略易读,断言类型绝对更少却不失性能;
- springboot 默认依赖了 hamcrest 库,依赖关系如下图:
- 一个简略的基于 hamcrest 的匹配器的单元测试代码如下,因为预期和理论的值不相等,因而会匹配失败:
package com.bolingcavalry.assertassume.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
@SpringBootTest
@Slf4j
public class HamcrestTest {
@Test
@DisplayName("体验 hamcrest")
void assertWithHamcrestMatcher() {assertThat(Math.addExact(1, 2), is(equalTo(5)));
}
}
- 执行后果如下:
- 以上就是 JUnit5 罕用的断言性能,心愿本篇能助您夯实根底,为后续写出更适合的用例做好筹备;
你不孤独,欣宸原创一路相伴
- Java 系列
- Spring 系列
- Docker 系列
- kubernetes 系列
- 数据库 + 中间件系列
- DevOps 系列
欢送关注公众号:程序员欣宸
微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游 Java 世界 …
https://github.com/zq2599/blog_demos
正文完