SpringBoot2 單元測試2
一、單元測試
首先是介紹下JUnit的相關概念。
概念 | 說明 |
Assert | 測試條件,當條件不成立時丟擲異常。如:Assert.assertSame(message, Expected, Actual)判斷Expected物件和Actual是否同一個物件(==),不同則丟擲異常提示message的資訊。 |
Suite | Suite允許將多個測試類歸成一組。在測試類(可空類)加註解@RunWith(Suite.class)和@SuiteClass({多個測試類})。 |
Runner | Runner類用來允許測試。JUnit沒有main()方法入口,其實在org.junit.runner包下有個JUnitCore.class,其中存在一個標準main方法,這就是JUnit的入口函式。 |
二、Dao層的單元測試
Dao層的測試需要準備一個空資料庫,以及一些初始化的資料,使用 @Sql 註解來初始化。
首先在classpath下準備一個初始化資料的指令碼檔案 user.sql ,內容如下:
INSERT INTO `user` (`user_id`, `name`) VALUES (99, '哈哈');
下面來看示例的測試程式碼:
// @ActiveProfiles啟用test作為profile,使用準備好的空資料庫 @ActiveProfiles("test") // @Transactional測試執行後回滾資料 @Transactional // ‘/’開頭表示從classpath根目錄開始搜尋,沒有以此開頭預設在測試類所在包下。也可使用classpath:、file:、http: 開頭 @Sql({"/user.sql"}) // @Runwith是JUnit標準的一個註解,Spring的單元測試都用SpringRunner.class @RunWith(SpringRunner.class) // @SpringBootTest用於Spring Boot應用測試,預設根據報名逐級往上尋找應用啟動類 @SpringBootTest public class UserDaoTest { @Autowired private UserDao userDao; @Test public void testGetOne() { User user = userDao.getOne(99); Assert.assertNotNull(user); Assert.assertEquals("哈哈", user.getName()); Assert.assertSame("不一樣呀", 99, user.getId()); } }
三、Service層的單元測試
Service層是處理業務邏輯的地方,通常比較複雜,編寫單元測試程式碼前需要處理好以下三個問題:
保證可重複測試。一個service方法可以多次測試,因此測試完畢後資料要能自動回滾。
模擬未完成的Service。當前測試的Service依賴的其它Service未開發完畢時,要能模擬其它Service。
準備測試資料。單元測試前要模擬好測試場景的資料。
如何解決呢?
對於第一個問題,可以使用Spring提供的 @Transactional 註解進行事務回滾。
對於第二個問題,使用Spring Boot整合的 Mockito 來模擬未完成的Service或者不能隨便呼叫的Service。
對於第三個問題,@Sql 註解再瞭解一下:)
下面來看一下使用 Mockito 解決第二個問題的示例。
假設現有 UserService 依賴於 CreditSystemService,但是CreditSystemService並沒有實現類
@Transactional
@SpringBootTest
@RunWith(SpringRunner.class)
public class UserServiceImplTest {
// UserService中依賴了CreditSystemService,但是CreditSystemService沒有實現類
@Autowired
private UserService userService;
// 註解@MockBean 可以自動注入Spring管理的Service,這裡creditSystemService由Mockito工具產生。
@MockBean
private CreditSystemService creditSystemService;
@Test
public void getCredit() throws Exception {
// given方法模擬一個service方法呼叫返回
BDDMockito.given(creditSystemService.getUserCredit(BDDMockito.anyInt())).willReturn(100);
Assert.assertEquals(100, userService.getCredit(userId));
}
}
四、Web層的單元測試
Spring MVC Test 通過 @WebMvcTest 來完成MVC單元測試。示例程式碼如下:
@RunWith(SpringRunner.class)
// @WebMvcTest表示這是一個MVC測試,可傳入多個待測試的Controller。
@WebMvcTest(UserController.class)
public class UserControllerTest {
// MockMvc是Spring專門提供用於測試SpringMVC類
@Autowired
private MockMvc mockMvc;
// @MockBean 用來模擬實現,因為在Spring MVC測試中並不會初始化@Service註解的類,需要自己模擬service實現。
@MockBean
private UserService userService;
@Test
public void getUser() throws Exception {
int userId = 1;
BDDMockito.given(userService.getCredit(userId)).willReturn(100);
// perform,完成一次MVC呼叫,Spring MVC Test是servlet容器內部的模擬測試,不會發起真正的HTTP請求。
// get,模擬一次GET請求。
// andExpect,表示請求期望的返回結果
mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", userId))
.andExpect(MockMvcResultMatchers.content().string(String.valueOf(100)));
}
}
MockMvc的核心方式是 public ResultActions perform(RequestBuilder requestBuilder) ,下面是一些模擬請求示例:
模擬GET請求:
mockMvc.perform(MockMvcRequestBuilders.get("/user/{id}", userId));
模擬Post請求:
mockMvc.perform(MockMvcRequestBuilders.post("uri", parameters));
模擬檔案上傳:
mockMvc.perform(MockMvcRequestBuilders.multipart("uri").file("fileName", "file".getBytes("UTF-8")));
模擬session和cookie:
mockMvc.perform(MockMvcRequestBuilders.get("uri").sessionAttr("name", "value"));
mockMvc.perform(MockMvcRequestBuilders.get("uri").cookie(new Cookie("name", "value")));
設定HTTP Header:
mockMvc.perform(MockMvcRequestBuilders
.get("uri", parameters)
.contentType("application/x-www-form-urlencoded")
.accept("application/json")
.header("", ""));
Mockito的用法非常多,這裡就不再展示了。