<dependency>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.hamcrestgroupId>
<artifactId>hamcrest-coreartifactId>
exclusion>
exclusions>
dependency>
在SpringBoot中使用JUnit的方式也发生了改变
以前我们要在SpringBoot中使用JUnit:采用 @SpringBootTest + @RunWith(SpringTest.class)
的方式
现在,在测试文件中通过@SpringBootTest注解标记一个SpringBoot的单元测试类
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Test
void contextLoads() {
}
}
总结一下新版本之后,SpringBoot整合JUnit的食用方法:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
此时,JUnit类具有Spring的功能
通过代码及运行结果查看具体应用
package com.atguigu.admin;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.concurrent.TimeUnit;
/**
* @author Bonbons
* @version 1.0
* @SpringBootTest相当于下面的两个注解
* @BootstrapWith(SpringBootTestContextBootstrapper.class)
* @ExtendWith({SpringExtension.class})
*/
@DisplayName("JUnit5功能测试类")
@SpringBootTest
public class JUnit5Test {
@Test
@DisplayName("测试displayname注解")
void testDisplayName(){
System.out.println(1);
System.out.println(jdbcTemplate);
}
@BeforeEach
void testBeforeEach(){
System.out.println("测试就要开始啦......");
}
@AfterEach
void testAfterEach(){
System.out.println("测试结束啦......");
}
@Disabled
@DisplayName("测试方法2")
@Test
void test2(){
System.out.println(2);
}
@BeforeAll
static void testBeforeAll(){
System.out.println("所有测试都要开始了...");
}
@AfterAll
static void testAfterAll(){
System.out.println("所有测试已经结束了...");
}
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
@Test
void testTimeout() throws InterruptedException{
Thread.sleep(555);
}
@RepeatedTest(5)
void test3(){
System.out.println(3);
}
//自动装配:整合SpringBoot之后才能使用
@Autowired
JdbcTemplate jdbcTemplate;
}
断言(assertions)是测试方法中的核心部分,用来测试需要满足的条件进行验证【可以理解为我们断定某个事件一定会发生】
以下按不同类别的断言展开叙述
方法 | 功能 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
@Test
@DisplayName("简单断言测试")
void testSimpleAssertions(){
//调用函数完成两数相加运算
int cal = cal(3, 3);
//assertEquals 的第一个参数是期望值,第二个参数是真实值
assertEquals(6, cal, "业务逻辑计算失败");
assertNotEquals(2, 1, "两个属性值相等");
//比较两个对象是不是指向同一个引用
assertSame(new Object(), new Object(), "两个对象不一样");
assertNotSame(new Object(), new Object(), "两个对象一样");
//判断是否为True、False
assertTrue(true, "布尔值为false,与期望值相反");
assertFalse(false, "布尔值为true,与期望值相反");
//判断是否为null、notNull
assertNull(null, "对象的引用不为空");
assertNotNull(null, "对象的引用为空");
}
int cal(int a, int b){
return a + b;
}
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等【就是比较目标数组与实际数组是否每个索引位置对应的值是否完全相同】
@Test
@DisplayName("数组断言测试")
void array(){
//判断两个数组是否相等 >> true 比较的是内容 [第一个数组是期望数组,第二个数组是实际数组]
assertArrayEquals(new int[]{1, 2}, new int[]{1, 2}, "数组内容不一致");
}
(3)组合断言
assertAll 方法接收多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易提供这些断言 【就是以与的方式组合判断多个断言】
assertAll 的参数都代表什么呢?
存在多个断言,前面的断言失败,后面的代码不会执行
对于组合断言,要想添加断言失败的消息,也可以在每个子断言中添加 message
@Test
@DisplayName("组合断言测试")
void all(){
assertAll("test",
() -> assertTrue(true & true),
() -> assertEquals(1, 1));
}
@Test
@DisplayName("测试异常断言")
void testException(){
assertThrows(ArithmeticException.class, () -> {
int num = 10 / 2;
}, "程序没抛出指定异常");
}
@Test
@DisplayName("超时测试")
public void timeoutTest() {
//如果测试方法时间超过1s将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(5000));
}
@Test
@DisplayName("测试快速失败")
void testFail(){
fail("测试失败");
}
@Test
@DisplayName("测试前置条件")
void testAssumptionsTrue(){
Assumptions.assumeTrue(false, "前置条件需要为true");
System.out.println("前置条件通过");
}
通过@Nested 注解 + 内部类的形式实现嵌套测试
在内部类中可以使用 @BeforeEach、@AfterEach、@BeforeAll、@AfterAll,而且嵌套层数没有限制
我们需要知道:
@DisplayName("A stack")
class TestingAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
将所有想测试的参数统一起来,会将这些参数挨个测试一遍 【属于JUnit5的新特性】
通过多种注解声明参数来源
只要实现 ArgumentsProvider 接口,任何外部文件都可以作为它的入参
@DisplayName("参数化测试")
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testParameterized(int i){
System.out.println(i);
}
static Stream<String> stringProvider(){
return Stream.of("apple", "pear", "banana");
}
@ParameterizedTest
@DisplayName("参数化测试2")
@MethodSource("stringProvider")
void testParameterized2(String s){
System.out.println(s);
}