欢送拜访我的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
发表回复