Java Spring Boot實戰練習之單元測試篇
一、關於JUnit的一些東西
在我們開發Web應用時,經常會直接去觀察結果進行測試。雖然也是一種方式,但是並不嚴謹。作為開發者編寫測試代碼來測試自己所寫的業務邏輯是,以提高代碼的質量、降低錯誤方法的概率以及進行性能測試等。經常作為開發這寫的最多就是單元測試。引入spring-boot-starter-testSpringBoot的測試依賴。該依賴會引入JUnit的測試包,也是我們用的做多的單元測試包。而Spring Boot在此基礎上做瞭很多增強,支持很多方面的測試,例如JPA,MongoDB,Spring MVC(REST)和Redis等。
接下來主要是測試業務邏輯層的代碼,REST和Mock測試。
1.1 JUnit介紹
JUnit是一個Java語言的單元測試框架。它由Kent Beck和Erich Gamma建立,逐漸成為源於Kent Beck的sUnit的xUnit傢族中最為成功的一個。 JUnit有它自己的JUnit擴展生態圈。多數Java的開發環境都已經集成瞭JUnit作為單元測試的工具。
JUnit相關概念 | 含義 |
---|---|
測試 | 一個以@Test註釋的方法定義一個測試,運行這個方法,JUnit會創建一個包含類的實例,然後再調用這個被註釋的方法。 |
測試類 | 包含多個@Test方法的一個類 |
Assert | 定義想測試的條件,當條件成立時,assert 方法保持沉默,條件不成立時,則拋出異常 |
Suite | Suite允許將測試類歸類成一組 |
Runner | Runner類用於運行測試,JUnit4是向後兼容的,可以運行JUnit3的測試實例 |
這裡使用的是JUnit4.x版本,JUnit中有兩個重要的類Assume+Assert,以及重要的註解:BeforeClass、AfterClass、After、Before、Test和Ignore。BeforeClass和AfterClass在每個類的開始和結束的時候運行,需要static修飾方法。而Before和After則是在每個測試方法的開始和結束的時候運行。
代碼片段:TestDeployApplication.class是自己編寫的Spring Boot啟動類。
@RunWith(SpringRunner.class) @SpringBootTest(classes = {TestDeployApplication.class}) public class UnitTest1 { @BeforeClass public static void beforeClass() { System.out.println("=================BeforeClass================"); } @AfterClass public static void afterClass() { System.out.println("=================AfterClass================"); } @Before public void beforeTest() { System.out.println("before test"); } @After public void afterTest() { System.out.println("after test"); } @Test public void test1() { System.out.println("test1"); } @Test public void test2() { System.out.println("test2"); } }
1.2 JUnit的Assert類
Assert類中常用的方法:
- assertEquals(“提示信息”,A,B):當判斷A是否等於B,不等於就拋出錯誤。比較對象是調用的是equals()方法。
- assertSame(“提示信息”,A,B):判斷對象是否相同。
- assertTrue(“提示信息”,A):判斷條件A是否為真。
- assertFalse(“提示信息”,A):判斷條件是否為假。
- assertNotNull(“提示信息”,A):判斷對象是否不為空。
- assertNull(“提示信息”,A):判斷對象是否不為空。
- assertArrayEqual(“提示信息”,A,B):判斷數組A和數組B是否相等。
1.3 JUnit的Suite
JUnit的Suite設計就是一次性運行一個或多個測試用例,Suite可以看作是一個容器,用來把測試類歸類在一起,並把他們作為一個集合來運行,運行器啟動Suite。
@RunWith(Suite.class) @SuiteClasses({UnitTest1.class,UnitTest2.class}) public class MainTest{ }
二、Spring Boot單元測試
添加需要的依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2.1 Spring Boot測試依賴提供的測試范圍
引入瞭spring-boot-starter-test繼承瞭很多的測試庫:
- JUnit,標準的單元測試Java程序。
- Spring Test和Spring Boot Test,對Spring Boot應用的單元測試。
- Mockito,Java Mock測試框架,用於模擬任何Spring管理的Bean。例如在- – 單元測試中,模擬一個第三方系統接口返回的數據,而不用真正地去請求第三方接口。
- AssertJ,一個assertion庫,同時提供瞭更加多的期望值與測試返回值的比較方式。
- Hamcrest,庫的匹配對象。
- JSONassert,對JSON對象或者JSON字符串斷言的庫。
- JSONPath,提供向XPath那樣的符號來獲取JSON字段。
2.2 Spring Boot單元測試的腳手架
在使用spring.io創建的Spring Boot工程中,就默認常見瞭一個單元測試的類。
@RunWith(SpringRunner.class) @SpringBootTest public class UnitTest1 { @Test public void contextLoads(){ } }
@RunWith是JUnit中的註解,用來通知JUnit單元測試框架不要使用內置的方式進行單元測試,向上面的寫法,就是指定使用SpringRunner類來提供單元測試。
@SpringBootTest註解則是用於Spring Boot應用的測試,默認會分局報名逐級往上查找Spring Boot主程序,也就是@SpringBootApplocation註解,並在單元測試啟動的時候啟動該類來創建Spring上下文。所以我們在對Spring Boot應用進行單元測試的時候,在日志輸出都可以看到Spring Boot應用的啟動日志。
2.3 對Service層代碼測試
import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.*; @RunWith(SpringRunner.class) @SpringBootTest @Transactional public class ServiceUnitTest { @MockBean private ThirdSystemService thirdSystemService; @Autowired private ISysUserService userService; @Test public void test1() { Long expectResult = 100L; given(thirdSystemService.develop()).willReturn(expectResult); SysUser sysUser = userService.findById(expectResult); System.out.println(sysUser.toString()); } }
@MockBean可以獲取在Spring下上文管理的Bean,但是thirdSystemService這個Bean並不是真的實列,而是通過Mockito工具創建的測試實例。通過@MockBean註解模擬出來的Bean,調用方法是不會真正的調用真正的方法,適用於在依賴瞭第三方的系統,然而第三方的系統的對接並沒有實現完成,自己可以單獨測試自己的業務代碼。willReturn(expectResult)說明結果永遠返回100L。
2.4 測試MVC代碼
Spring Boot中還能單獨測試Controller的代碼,例如測試Controller中方法的參數綁定和校驗之類的邏輯。可以通過@WebMvcTest註解來完成單元測試。
@RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; @Test public void test2() throws Exception { MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/hello/{id}", 1L); mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } }
像Get方法傳遞參數
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders .get("/hello/{id}", 1L) // path變量 .param("name", "hello"); // @RequestParam 獲取變量。post請求也適用
文件上傳
@RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; @Test public void test3() throws Exception { // 獲取文件 FileInputStream fileInputStream = new FileInputStream("文件路徑"); // 構建文件上傳對象 MockMultipartFile mockMultipartFile = new MockMultipartFile("file", fileInputStream); // 構建mock文件上傳請求 MockMultipartHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.multipart("/upload").file(mockMultipartFile); // 發送請求 mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } }
模擬Cookie和Session @RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; @Test public void test4() throws Exception { MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders .get("index.html") .sessionAttr("name", "hello") .cookie(new Cookie("token", "123345")); mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } }
設置請求頭
@RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; @Test public void test5() throws Exception { MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders .get("index.html") .content(MediaType.APPLICATION_JSON_VALUE) // 期望返回類型 .contentType(MediaType.APPLICATION_JSON_VALUE) // 提交的內容類型 .header("token", 1235); // 設置請求頭 mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()); } }
2.5 比較返回結果
MockMvc類的perform方法會返回一個ResultAction類,可以對結果進行一些操作(andExpect、andDo和andReturn)。
@RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; @Test public void test2() throws Exception { MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders .get("/hello/{id}", 1L) .param("name", "hello"); mockMvc.perform(requestBuilder) .andExpect(MockMvcResultMatchers.jsonPath("$.id", "id").value(2L)); .andDo(MockMvcResultHandlers.print()); } }
例如上面獲取返回的JSON結果中的id字段的值,value是期望值,如果期望值與實際值不一樣測試就會報錯。
也可以斷言測試返回結果的View(視圖)和Model(數據模型)是否是期望值
@RunWith(SpringRunner.class) @WebMvcTest(SysUserController.class) public class ServiceUnitTest { @Autowired private MockMvc mockMvc; MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders .get("/hello/{id}", 1L) .param("name", "hello"); mockMvc.perform(requestBuilder) // 斷言返回的試圖 .andExpect(MockMvcResultMatchers.view().name("index.html")) // 斷言返回的數據模型中的數據 .andExpect(MockMvcResultMatchers.model().attribute("id",1L)) .andDo(MockMvcResultHandlers.print()); }
更多的結果斷言可以在MockMvcResultMatchers
類中找到,該類是請求結果的匹配的一個工具類。
如果對軟件測試、接口測試、自動化測試、持續集成、面試經驗。感興趣 可以進到806549072,群內會有不定期的分享測試資料。還會有技術大牛,業內同行一起交流技術
到此這篇關於Java Spring Boot實戰練習之單元測試篇的文章就介紹到這瞭,更多相關Java Spring Boot 單元測試內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 單元測試 @mock與@SpringBootTest的使用
- SpringBoot單元測試使用@Test沒有run方法的解決方案
- SpringMVC框架整合Junit進行單元測試(案例詳解)
- SpringMVC框架如何與Junit整合看這個就夠瞭
- Java單元測試Mockito的使用詳解