單元測試利器 Mockito

前言

Mockito是當前最流行的單元測試Mock框架。咱們能夠虛擬出一個外部依賴,下降測試組件之間的耦合度,只注重代碼的流程與結果,真正地實現測試目的。spring

什麼是Mockito

Mock的中文譯爲仿製的,模擬的,虛假的。對於測試框架來講,即構造出一個模擬/虛假的對象,使咱們的測試能順利進行下去。json

ito 實際上是input to outputspringboot

Mockito 大體意思就是模擬輸入和輸出,用一個 虛擬的對象( Mock對象)來建立,以便測試方法。

爲何使用Mock測試

單元測試是爲了驗證咱們的代碼運行正確性,咱們注重的是代碼的流程以及結果的正確與否。mvc

對比真實運行代碼,可能其中有一些外部依賴的構建步驟相對麻煩,測試用例顯得複雜難懂,會大大增長單元測試的工做。app

使用Mock,咱們能夠虛擬出一個外部依賴,只注重代碼的流程與結果,真正地實現測試目的。框架

使用Mock測試的好處

  1. 能夠很簡單的虛擬出一個複雜對象(好比虛擬出一個接口的實現類);
  2. 可使測試用例只注重測試流程與結果;
  3. 減小外部類、系統和依賴給單元測試帶來的耦合。
  4. 團隊能夠並行工做(好比A依賴B,但B並無開發,此時就能夠Mock出一個B

使用Mockito測試示例

C層代碼:ide

@RestController
@RequestMapping("Student")
public class StudentController {
    @Autowired
    StudentService studentService; 
    
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Student save(@RequestBody Student student) {
        return studentService.save(student); 
    }
}

M層代碼:post

  • 接口單元測試

    public interface StudentService {
        Student save(Student student);
    }
  • 實現類:測試

    @Service
    public class StudentServiceImpl implements StudentService {
        @Autowired
        StudentRepository studentRepository;
        @Override
        public Student save(Student student) {
            return studentRepository.save(student);
        }
    }
以save這個方法爲例,進行 C M 層測試

測試C層:

  1. 模擬發送請求
    image.png
  2. 測試傳入的值是否符合預期
    image.png
  3. 測試返回的值是否符合預期
    image.png
public class StudentControllerTest {
    @Test
    public void save() throws Exception {
    
        // 模擬發送請求 

        logger.info("準備輸入數據");
        String url = "Student";
        JSONObject studentJsonObject = new JSONObject();
        JSONObject klassJsonObject = new JSONObject();
        studentJsonObject.put("sno", "學號測試");
        studentJsonObject.put("name", "姓名測試");
        klassJsonObject.put("id", -1);
        studentJsonObject.put("klass", klassJsonObject);

        logger.info("發起請求");
        MvcResult mvcResult = this.mockMvc.perform(
                MockMvcRequestBuilders.post(url)
                    .content(studentJsonObject.toString()
                    .contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().is(201)
                .andDo(MockMvcResultHandlers.print())
                .andReturn();

        // 測試傳入的值是否符合預期
        
        logger.info("新建參數捕獲器");
        ArgumentCaptor<Student> studentArgumentCaptor = ArgumentCaptor.forClass(Student.class);
        Mockito.verify(studentService).save(studentArgumentCaptor.capture());

        Student passedStudent = studentArgumentCaptor.getValue();

        logger.info("斷言捕獲的對與咱們前面傳入的值的相同");             
        Assertions.assertThat(passedStudent.getSno()).isEqualTo("學號測試");
        Assertions.assertThat(passedStudent.getName()).isEqualTo("姓名測試");
        Assertions.assertThat(passedStudent.getId()).isNull();
        Assertions.assertThat(passedStudent.getKlass().getId()).isEqualTo(-1L);
        
        // 測試返回的值是否符合預期
        
        logger.info("準備服務層被調用後的返回數據");
        Student returnStudent = new Student();
        returnStudent.setId(1L);
        returnStudent.setSno("測試返回學號");
        returnStudent.setName("測試返回姓名");
        returnStudent.setKlass(new Klass());
        returnStudent.getKlass().setId(1L);
       
        Mockito.when(studentService.save(Mockito.any(Student.class))).thenReturn(returnStudent);

        logger.info("獲取返回的值並斷言此值與咱們模擬的返回值相同");
        String stringReturn = mvcResult.getResponse().getContentAsString();
        DocumentContext documentContext = JsonPath.parse(stringReturn);
        LinkedHashMap studentHashMap = documentContext.json();
        Assertions.assertThat(studentHashMap.get("id")).isEqualTo(1);
        Assertions.assertThat(studentHashMap.get("sno")).isEqualTo("測試返回學號");
        Assertions.assertThat(studentHashMap.get("name")).isEqualTo("測試返回姓名");

        LinkedHashMap klassHashMap = (LinkedHashMap) studentHashMap.get("klass");
        Assertions.assertThat(klassHashMap.get("id")).isEqualTo(1);
    }
}

測試M層:

  • M層單元測試相對比較簡單,不須要模擬前臺的數據,邏輯更加清晰
    image.png
public class StudentServiceImplTest {
    @Test
    public void save() {
    
        logger.info("準備傳入的數據");
        Student passStudent = new Student();

        logger.info("調用服務層");
        Student returnStudent = studentService.save(passStudent);
             
        logger.info("準備返回的數據");  
        Student mockReturnStudent = new Student();
        Mockito.when(studentRepository.save(Mockito.any(Student.class))).thenReturn(mockReturnStudent);

        logger.info("新建參數捕獲器");
        ArgumentCaptor<Student> studentArgumentCaptor =ArgumentCaptor.forClass(Student.class);
        Mockito.verify(studentRepository).save(studentArgumentCaptor.capture());

        logger.info("斷言獲取的數據與傳入的相同");
        Assertions.assertThat(studentArgumentCaptor.getValue()).isEqualTo(passStudent);
        
        logger.info("斷言返回的數據與輸出的相同");
        Assertions.assertThat(returnStudent).isEqualTo(mockReturnStudent);
    }
}

總結

總感受當時能搞明白啥意思,可是時間一長本身仍是有點懵,還須要捋一下思路才行,全部在此簡單記錄總結一下。

參考SpringBoot+Angular入門實例教程[4.5.9] 關於更多詳細的SpringBootAngular 的知識教程都有提到,有興趣的能夠閱讀。

相關文章
相關標籤/搜索