SpringBoot實(shí)現(xiàn)單元測(cè)試示例詳解
一、常用注解
- @Test :表示此方法是測(cè)試方法。但是與JUnit4的@Test不同,他的職責(zé)非常單一,不能聲明任何屬性,拓展的測(cè)試將會(huì)由Jupiter提供額外測(cè)試
- @ParameterizedTest:參數(shù)化測(cè)試使用注解
- @RepeatedTest :表示測(cè)試方法可重復(fù)執(zhí)行,value表示重復(fù)執(zhí)行次數(shù)
- @DisplayName :為測(cè)試類或者測(cè)試方法設(shè)置展示名稱
- @BeforeEach :表示在每個(gè)單元測(cè)試之前執(zhí)行該方法
- @AfterEach :表示在每個(gè)單元測(cè)試之后執(zhí)行該方法
- @BeforeAll :表示在所有開始單元測(cè)試之前執(zhí)行,此方法必須是靜態(tài)方法
- @AfterAll :表示在所有單元測(cè)試完成之后執(zhí)行,此方法必須是靜態(tài)方法
- @Tag :表示單元測(cè)試類別,類似于JUnit4中的@Categories
- @Disabled :表示測(cè)試類或測(cè)試方法不執(zhí)行,類似于JUnit4中的@Ignore
- @Timeout :表示測(cè)試方法運(yùn)行如果超過了指定時(shí)間將會(huì)返回錯(cuò)誤
- @SpringBootTest:如果測(cè)試類想要使用Spring Boot的自動(dòng)注入功能,例如@Autowired注解等,就需要在測(cè)試類上加上此注解
- @ExtendWith :為測(cè)試類或測(cè)試方法提供擴(kuò)展類引用,類似于@RunWith,@RunWith(JUnit4.class) 就是指用JUnit4來運(yùn)行,@RunWith(SpringJUnit4ClassRunner.class),讓測(cè)試運(yùn)行于Spring測(cè)試環(huán)境
package com.decade;
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;
@DisplayName("測(cè)試類的名稱為MyTest")
@SpringBootTest
public class MyTest {
@Autowired
private JdbcTemplate jdbcTemplate;
@DisplayName("測(cè)試方法名稱為test")
@RepeatedTest(value = 3)
public void test() {
System.out.println("測(cè)試方法,打印JdbcTemplate類" + jdbcTemplate);
}
@Test
@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)
public void testTimeOut() throws InterruptedException {
Thread.sleep(600);
}
@BeforeEach
public void beforeEach() {
System.out.println("每個(gè)方法前執(zhí)行");
}
@AfterEach
public void afterEach() {
System.out.println("每個(gè)方法后執(zhí)行");
}
@BeforeAll
static void beforeAll() {
System.out.println("所有方法前執(zhí)行");
}
@AfterAll
static void afterAll() {
System.out.println("所有方法后執(zhí)行");
}
@Disabled
@Test
public void disableTest() {
System.out.println("此內(nèi)容不輸出");
}
}二、斷言機(jī)制
斷言是測(cè)試方法中的核心部分,它用于檢查業(yè)務(wù)邏輯返回的數(shù)據(jù)是否合理,不滿足的斷言會(huì)使得測(cè)試方法失敗
如果是多個(gè)斷言依次執(zhí)行,只要前面的斷言不通過,后面的就不會(huì)再執(zhí)行了
1、簡(jiǎn)單斷言
- assertEquals:判斷兩個(gè)對(duì)象或兩個(gè)原始類型是否相等
- assertNotEquals:判斷兩個(gè)對(duì)象或兩個(gè)原始類型是否不相等
- assertSame:判斷兩個(gè)對(duì)象引用是否指向同一個(gè)對(duì)象
- assertNotSame:判斷兩個(gè)對(duì)象引用是否指向不同的對(duì)象
- assertTrue:判斷給定的布爾值是否為 true
- assertFalse:判斷給定的布爾值是否為 false
- assertNull:判斷給定的對(duì)象引用是否為 null
- assertNotNull:判斷給定的對(duì)象引用是否不為 null
2、數(shù)組斷言
通過 assertArrayEquals 方法來判斷兩個(gè)對(duì)象或原始類型的數(shù)組是否相等
3、組合斷言
通過assertAll方法接受多個(gè)函數(shù)式接口的實(shí)例作為要驗(yàn)證的斷言,只要有一個(gè)不通過,就算失敗
4、異常斷言
通過assertThrow方法斷定某個(gè)代碼會(huì)拋出指定異常
5、超時(shí)異常
通過assertTimeout斷定某個(gè)代碼的執(zhí)行時(shí)間會(huì)超過限制時(shí)間
6、快速失敗
通過fail方法直接使得測(cè)試失敗
package com.decade;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
@DisplayName("測(cè)試類為TestAssert")
public class TestAssert {
@Test
@DisplayName("測(cè)試普通斷言")
public void testSimpleAssert() {
Assertions.assertEquals(6, 2+3, "期望值與實(shí)際值不符");
Assertions.assertTrue(1 > 2, "條件不成立");
Object a = new Object();
Object b = new Object();
Assertions.assertSame(a, b, "不是同一個(gè)對(duì)象");
}
@Test
@DisplayName("測(cè)試數(shù)組斷言")
public void testArray() {
Assertions.assertArrayEquals(new int[]{1, 2}, new int[]{2,1}, "數(shù)組不相等");
}
@Test
@DisplayName("測(cè)試組合斷言")
public void testCombination() {
Assertions.assertAll("斷言組合1",
() -> Assertions.assertTrue(1 < 2, "判斷條件不成立"),
() -> Assertions.assertEquals(2, 3, "期望值與實(shí)際值不相符"));
}
@Test
@DisplayName("測(cè)試異常斷言")
public void testException() {
Assertions.assertThrows(ArithmeticException.class, () -> {
System.out.println(1/0);
}, "并沒有拋出算數(shù)異常");
}
@Test
@DisplayName("測(cè)試超時(shí)斷言")
public void testTimeOut() {
Assertions.assertTimeout(Duration.of(100, ChronoUnit.MILLIS),
() -> Thread.sleep(500), "超時(shí)");
}
@Test
@DisplayName("測(cè)試快速失敗fail")
public void shouldFail() {
Assertions.fail("This should fail");
}
}三、前置條件
前置和斷言的不同之處在于,不滿足的斷言會(huì)使得測(cè)試方法失敗,而不滿足的前置條件只會(huì)使得測(cè)試方法的執(zhí)行終止
前置條件可以看成是測(cè)試方法執(zhí)行的前提,當(dāng)該前提不滿足時(shí),就沒有繼續(xù)執(zhí)行的必要
package com.decade;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("測(cè)試類為Test")
public class Test {
@Test
@DisplayName("測(cè)試前置條件")
public void testAssumptions() {
Assumptions.assumeTrue(1 > 2, "條件不成立,后續(xù)步驟不再執(zhí)行");
System.out.println("前置條件成立,繼續(xù)執(zhí)行到此步驟");
}
}四、嵌套測(cè)試
可以理解成測(cè)試套娃,需要注意的是,外層的測(cè)試方法無法驅(qū)動(dòng)內(nèi)層的測(cè)試方法,去執(zhí)行內(nèi)層測(cè)試方法的beforeEach、beforeAll、afterEach、afterAll
package com.decade;
import org.junit.jupiter.api.*;
import java.util.EmptyStackException;
import java.util.Stack;
@DisplayName("進(jìn)行嵌套測(cè)試")
public class TestAStackDemo {
Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
// 外層的test方法無法驅(qū)動(dòng)內(nèi)層的test,也就無法驅(qū)動(dòng)下方的BeforeEach去實(shí)例化stack,所以這里會(huì)報(bào)空
Assertions.assertNull(stack);
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
Assertions.assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
Assertions.assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
Assertions. assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
String anElement = "an element";
// 內(nèi)層的test可以驅(qū)動(dòng)外層的test,這里會(huì)驅(qū)動(dòng)上方的beforeEach去實(shí)例化stack
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
Assertions.assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
Assertions.assertEquals(anElement, stack.pop());
Assertions.assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
Assertions.assertEquals(anElement, stack.peek());
Assertions.assertFalse(stack.isEmpty());
}
}
}
}五、參數(shù)化測(cè)試
- @ValueSource: 為參數(shù)化測(cè)試指定入?yún)碓?,支持八大基礎(chǔ)類以及String類型,Class類型
- @NullSource: 表示為參數(shù)化測(cè)試提供一個(gè)null的入?yún)?/li>
- @EnumSource: 表示為參數(shù)化測(cè)試提供一個(gè)枚舉入?yún)?/li>
- @CsvFileSource:表示讀取指定CSV文件內(nèi)容作為參數(shù)化測(cè)試入?yún)?/li>
- @MethodSource:表示讀取指定方法的返回值作為參數(shù)化測(cè)試入?yún)?注意方法返回需要是一個(gè)流)
package com.decade;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.stream.Stream;
@DisplayName("進(jìn)行參數(shù)化測(cè)試")
public class Test {
@ParameterizedTest
@ValueSource(strings = {"decade", "hhh", "LOL"})
@DisplayName("參數(shù)化測(cè)試,傳入String類型依次測(cè)試")
public void testParameter(String variables) {
System.out.println(variables);
}
@ParameterizedTest
// 此處為方法名,方法必須返回stream流,并且是靜態(tài)的
@MethodSource("creatStream")
@DisplayName("參數(shù)化測(cè)試,傳入String類型依次測(cè)試")
public void testParameterIsMethod(String variables) {
System.out.println(variables);
}
static Stream<String> creatStream() {
return Stream.of("decade", "hhh", "basketball");
}
}測(cè)試結(jié)果如圖

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)單元測(cè)試示例詳解的文章就介紹到這了,更多相關(guān)SpringBoot單元測(cè)試內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Netty啟動(dòng)流程服務(wù)端channel初始化源碼分析
這篇文章主要為大家介紹了Netty啟動(dòng)流程服務(wù)端channel初始化源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
java網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)介紹
這篇文章主要介紹了java網(wǎng)絡(luò)編程基礎(chǔ)知識(shí)介紹,涉及OSI分層模型和TCP/IP分層模型的對(duì)應(yīng)關(guān)系、IP地址、端口號(hào)、tcp、udp等相關(guān)內(nèi)容,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-11-11
SpringBoot實(shí)現(xiàn)RabbitMQ監(jiān)聽消息的四種方式
本文主要介紹了SpringBoot實(shí)現(xiàn)RabbitMQ監(jiān)聽消息的四種方式,包括@RabbitListener,MessageListener接口,MessageListenerAdapter適配器,@RabbitHandler這幾種,感興趣的可以了解一下2024-05-05
Java Swing實(shí)現(xiàn)坦克大戰(zhàn)游戲
這篇文章主要介紹了Java Swing實(shí)現(xiàn)坦克大戰(zhàn)游戲,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很大的幫助喲,需要的朋友可以參考下2021-05-05

