1. 程式人生 > >SpringBoot2 單元測試2

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的用法非常多,這裡就不再展示了。