日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

SpringBoot Test及注解详解(含Mockito)

發(fā)布時間:2025/3/15 javascript 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 SpringBoot Test及注解详解(含Mockito) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、版本差異

Spring Boot 2.2.0 版本開始引入 JUnit 5 作為單元測試默認庫,在 Spring Boot 2.2.0 版本之前,spring-boot-starter-test 包含了 JUnit 4 的依賴,Spring Boot 2.2.0 版本之后替換成了 Junit Jupiter。

  • pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>

導入的依賴如下:

可以看到,SpringBootTest默認集成了以下功能:

  • JUnit 5: Java單元測試框架
  • Spring Test & Spring Boot Test: Spring Boot的測試工具和支持
  • AssertJ: 流式斷言
  • Hamcrest: Hamcrest斷言
  • Mockito: Java Mock框架
  • JSONassert: JSON斷言
  • JsonPath: XPath for JSON

二、SpringBootTest和Junit5的使用

整體上,Spring Boot Test支持的測試種類,大致可以分為如下三類:

  • 單元測試:一般面向方法,編寫一般業(yè)務代碼時,測試成本較大。涉及到的注解有@Test。
  • 切片測試:一般面向難于測試的邊界功能,介于單元測試和功能測試之間。涉及到的注解有 @WebMvcTest等。主要就是對于Controller的測試,分離了Service層,這里就涉及到Moc控制層所依賴的組件了
  • 功能測試:一般面向某個完整的業(yè)務功能,同時也可以使用切面測試中的mock能力,推薦使用。涉及到的注解有@SpringBootTest等。
  • 單元測試
  • 集成測試,不啟動server,以創(chuàng)建項目后自動生成的默認測試類為例:

    @SpringBootTest class TestDemoApplicationTests {@Testvoid contextLoads() {} }

    默認無參數(shù)的@SpringBootTest 注解會加載一個Web Application Context并提供Mock Web Environment,但是不會啟動內置的server。這點從日志中沒有打印Tomcat started on port(s)可以佐證。

  • 集成測試,啟動server
  • 新建一個測試類如下:

    //指定@SpringBootTest的Web Environment為RANDOM_PORT //此時,將會加載Applicaiton Context,并啟動server,server偵聽在隨機端口上。在測試類中通過@LocalServerPort獲取該端口值。 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class DemoTest {@LocalServerPortprivate Integer port;@Test@DisplayName("should access application")void shouldAccessApplication() {assertThat(port).isGreaterThan(1024);} }

    也可以通過指定@SpringBootTest的Web Environment為DEFINED_PORT 來指定server偵聽應用程序配置的端口,默認為8080。不過這種指定端口的方式很少使用,因為如果本地同時啟動應用時,會導致端口沖突。

  • 更多關系JUnit5集成SpringBootTest的例子,參考這個文檔,我這里不在啰嗦
  • 三、Spring Boot Test中的主要注解

  • 在說Mockito之前,先看一下SpringBootTest的注解,Mockito是一個獨立的框架,被springboot集成了而已。
  • 從功能上講,Spring Boot Test中的注解主要分如下幾類

    • 配置類型的注解:

    使用@SpringBootApplication啟動測試或者生產代碼,被@TestComponent描述的Bean會自動被排除掉。如果不是則需要向@SpringBootApplication添加TypeExcludeFilter。

    • mock類型的注解

      @MockBean和@SpyBean這兩個注解,在mockito框架中本來已經存在,且功能基本相同。Spring Boot Test又定義一份重復的注解,目的在于使MockBean和SpyBean被ApplicationContext管理,從而方便使用。

    MockBean和SpyBean功能非常相似,都能模擬方法的各種行為。不同之處在于MockBean是全新的對象,跟正式對象沒有關系;而SpyBean與正式對象緊密聯(lián)系,可以模擬正式對象的部分方法,沒有被模擬的方法仍然可以運行正式代碼。

    • 自動配置類型的注解(@AutoConfigure*)

    這些注解可以搭配@\*Test使用,用于開啟在@\*Test中未自動配置的功能。例如@SpringBootTest和@AutoConfigureMockMvc組合后,就可以注入org.springframework.test.web.servlet.MockMvc。

    “自動配置類型”有兩種使用方式:

  • 在功能測試(即使用@SpringBootTest)時顯示添加。
  • 一般在切片測試中被隱式使用,例如@WebMvcTest注解時,隱式添加了@AutoConfigureCache、@AutoConfigureWebMvc、@AutoConfigureMockMvc。
    • 啟動測試類型的注解

    所有的@*Test注解都被@BootstrapWith注解,它們可以啟動ApplicationContext,是測試的入口,所有的測試類必須聲明一個@*Test注解。

    除了@SpringBootTest之外的注解都是用來進行切面測試的,他們會默認導入一些自動配置,點擊官方docs查看詳情。一般情況下,推薦使用@SpringBootTest而非其它切片測試的注解,簡單有效。若某次改動僅涉及特定切片,可以考慮使用切片測試。SpringBootTest是這些注解中最常用的一個,其中包含的配置項如下:


    webEnvironment詳細說明:

    • 相似注解的區(qū)別和聯(lián)系
    • @TestComment vs @Comment:
      @TestComponent是另一種@Component,在語義上用來指定某個Bean是專門用于測試的。使用@SpringBootApplication服務時,@TestComponent會被自動排除
    • @TestConfiguration vs @Configuration :
      @TestConfiguration是Spring Boot Boot Test提供的,@Configuration是Spring Framework提供的。@TestConfiguration實際上是也是一種@TestComponent,只是這個@TestComponent專門用來做配置用。
      @TestConfiguration和@Configuration不同,它不會阻止@SpringBootTest的查找機制,相當于是對既有配置的補充或覆蓋。
    • @SpringBootTest vs @WebMvcTest(或@*Test) :
      都可以啟動Spring的ApplicationContext @SpringBootTest自動偵測并加載@SpringBootApplication或@SpringBootConfiguration中的配置,@WebMvcTest不偵測配置,只是默認加載一些自動配置。
      @SpringBootTest測試范圍一般比@WebMvcTest大。
    • @MockBean vs @SpyBean:
      都能模擬方法的各種行為。不同之處在于MockBean是全新的對象,跟正式對象沒有關系;而SpyBean與正式對象緊密聯(lián)系,可以模擬正式對象的部分方法,沒有被模擬的方法仍然可以運行正式代碼

    參考文章

    四、Mockito的使用

  • 簡單的一個例子
  • public class MyMockitoTest {private static UserServiceImpl mockUserService;private static List<String> mockedList;@BeforeAllpublic static void beforeMock() throws Exception {//使用Mock,模擬UserServiceImpl對象mockUserService = mock(UserServiceImpl.class);// mock creation 創(chuàng)建mock對象mockedList = mock(List.class);/** 默認情況下,所有的函數(shù)都有返回值。mock函數(shù)默認返回的是null,* 一個空的集合或者一個被對象類型包裝的內置類型,* 例如0、false對應的對象類型為Integer、Boolean*///做一些測試樁(stubbing),也即是定義行為,如果是getOneUser(3),則返回的是null,2則拋出異常when(mockUserService.getOneUser(1)).thenReturn(new User("a",1));//注意該拋出異常的stubbing,一定是UserServiceImpl真的有拋出這個異常,Mockito才能編譯通過,并執(zhí)行when(mockUserService.getOneUser(2)).thenThrow(new IllegalAccessException());when(mockUserService.getOneUser(3)).thenReturn(new User("a",1));when(mockUserService.update(isA(User.class))).thenReturn(true);}@Test@DisplayName("GetOneUser")public void testGet() throws Exception {//使用mock模擬出來的mockUserService進行操作User user = mockUserService.getOneUser(1);User oneUser = mockUserService.getOneUser(2);User oneUser1 = mockUserService.getOneUser(3);System.out.println(user);System.out.println(oneUser);mockUserService.update(user);//驗證是否執(zhí)行過一次getOneUser(1)verify(mockUserService, times(1)).getOneUser(eq(1));//驗證是否執(zhí)行過一次updateverify(mockUserService, times(1)).update(isA(User.class));}@Testpublic void testMatcher(){//使用內置的anyInt()參數(shù)匹配器,也可以使用自定義的參數(shù)處理器when(mockedList.get(anyInt())).thenReturn("element");System.out.println(mockedList.get(999));}//驗證函數(shù)的確切、最少、從未調用次數(shù)@Test@DisplayName("testUsingTime")public void testUsingTime(){//using mockmockedList.add("once");mockedList.add("twice");mockedList.add("twice");mockedList.add("three times");mockedList.add("three times");mockedList.add("three times");// 下面的兩個驗證函數(shù)效果一樣,因為verify默認驗證的就是times(1)// verify函數(shù)默認驗證的是執(zhí)行了times(1),也就是某個測試函數(shù)是否執(zhí)行了1次.因此,times(1)通常被省略了。verify(mockedList).add("once");verify(mockedList, times(1)).add("once");// 驗證具體執(zhí)行次數(shù)verify(mockedList, times(2)).add("twice");verify(mockedList, times(3)).add("three times");// 使用never()進行驗證,never相當于times(0)verify(mockedList, never()).add("never happened");// 使用atLeast()/atMost()verify(mockedList, atLeastOnce()).add("three times");verify(mockedList, atLeast(2)).add("five times");verify(mockedList, atMost(5)).add("three times");} }
  • 主要看一下使用mockito進行切面測試(Controller)
  • public class Keywords implements Serializable {private Integer id;private String keyword;private String notes;public Keywords(){}@Overridepublic String toString() {return "Keywords{" +"id=" + id +", keyword='" + keyword + '\'' +", notes='" + notes + '\'' +'}';}public Integer getId() {return id;}public String getKeyword() {return keyword;}public String getNotes() {return notes;}private Keywords(Builder builder){this.id=builder.id;this.keyword = builder.keyword;this.notes = builder.notes;}public static class Builder{private Integer id;private String keyword;private String notes;public Builder setId(Integer id) {this.id = id;return this;}public Builder setKeyword(String keyword) {this.keyword = keyword;return this;}public Builder setNotes(String notes) {this.notes = notes;return this;}public Keywords build(){return new Keywords(this);}} } @Controller public class KeywordController {@Autowiredprivate KeywordsService keywordsService;@Autowiredprivate KeywordsServiceImpl keywordsServiceImpl;@GetMapping(value = "/api/keywords")public Keywords findKeywordById(@RequestParam(value = "id") Integer id) {return keywordsService.findKeywordById(id);}@PostMapping("/api/add")@ResponseBodypublic Boolean addOne(@RequestBody Keywords keywords){//調用被spy注解的類的方法,就會直接使用真實的方法return keywordsServiceImpl.addOne(keywords);} } @Repository public interface KeywordsService {Keywords findKeywordById(int i);Boolean addOne(Keywords keywords); } @Service public class KeywordsServiceImpl implements KeywordsService {@Overridepublic Keywords findKeywordById(int i) {return null;}@Overridepublic Boolean addOne(Keywords keywords) {System.out.println("invoke spy class method");System.out.println(keywords);return false;} } public class MvcMockitoTest {//定義MockMvc對象protected MockMvc mockMvc;@Mock//要mock被測類中依賴的對象使用@Mock注解private KeywordsService keywordsService;@Spy//被 spy 的對象,調用其方法時默認會走真實方法。private KeywordsServiceImpl keywordsServiceImpl;@InjectMocks//被測類本身使用@InjectMocks注解private KeywordController controller;@BeforeEach()public void setup() {MockitoAnnotations.openMocks(this);//初始化MockMvc對象,將KeywordController加載進Spring容器mockMvc = MockMvcBuilders.standaloneSetup(controller).build();}@Test@DisplayName("findKeywordByIdTest")public void findKeywordByIdTest() throws Exception {Keywords keywords = new Builder().setId(666).setKeyword("tester").setNotes("notes").build();//打樁,當執(zhí)行findKeywordById(1)時,就返回上面創(chuàng)建的keywords對象Mockito.when(keywordsService.findKeywordById(1)).thenReturn(keywords);//執(zhí)行一個RequestBuider請求,自動執(zhí)行SpringMvc的流程并映射到相應的控制器執(zhí)行處理MvcResult mvcResult = mockMvc.perform(get("/api/keywords?id=1") //請求的url,請求的方法是Get.contentType(MediaType.APPLICATION_JSON)) //數(shù)據(jù)的格式//添加ResultMatcher驗證規(guī)則,驗證perform執(zhí)行完成后的結果是否正確(對返回的數(shù)據(jù)進行判斷).andExpect(status().isOk()) //期待的返回狀態(tài)是200//添加ResultHandler結果處理器,比如調試打印結果到控制臺print().andDo(print())//打印出請求和相應的內容//最后返回相應的MvcResult,然后進行自定義驗證/進行下一步的異步處理.andReturn();System.out.println(mvcResult.getResponse().getContentAsString());}@Test@DisplayName("addOne")public void testAddOne() throws Exception {Keywords build = new Builder().setId(1).setKeyword("addOne").setNotes("testAddOne").build();Gson gson = new Gson();String jsonString =gson.toJson(build);System.out.println(jsonString);MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/api/add").contentType(MediaType.APPLICATION_JSON)//發(fā)送的文本格式.content(jsonString).accept(MediaType.APPLICATION_JSON)//接受的文本格式).andExpect(MockMvcResultMatchers.status().isOk()).andDo(MockMvcResultHandlers.print()).andReturn();int status = mvcResult.getResponse().getStatus();assertEquals(status,200);System.out.println("輸出 " + mvcResult.getResponse().getContentAsString());} }

    結果:



    mockito可以配合junit5的斷言功能使用。更多用法可以參考官方文檔

    總結

    以上是生活随笔為你收集整理的SpringBoot Test及注解详解(含Mockito)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。