<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                前后臺均啟用MVC的設計模式后則會發現前后臺的開發思想大同小異。 # Thinking 正式動手前先畫個圖: ![](https://img.kancloud.cn/b0/62/b0623d918bede289c810098b9ec61d5b_556x203.png) 在開發順序上,后臺的開發仍然建立由后向前來完成。這樣以避免在使用一些不熟悉的方法時帶來的對接問題。 # M層 按先接口、再實現類再單元測試的步驟依次進行開發。 ## 接口 service/StudentService.java ```java /** * 刪除學生 * @param id 學生id */ void deleteById(Long id); ``` ## 實現類 service/StudentServiceImpl.java ```java @Override public void deleteById(@NotNull Long id) { Assert.notNull(id, "傳入的ID不能為NULL"); this.studentRepository.deleteById(id); } ``` ## 單元測試 service/StudentServiceImplTest ```java /** * 參數驗證 */ @Test public void deleteByIdValidate() { } /** * 功能測試 */ @Test public void deleteById() { // 替身及模擬返回值準備 // 調用方法 // 預測以期望的參數值調用了期望的方法 } ``` 補充參數校驗代碼:傳入null時發生IllegalArgumentException異常。 service/StudentServiceImplTest ```java @Test(expected = IllegalArgumentException.class) public void deleteByIdValidate() { this.studentService.deleteById(null); } ``` 補充功能測試代碼: ```java @Test public void deleteById() { // 替身及模擬返回值準備 Long id = new Random().nextLong(); // studentRepository.deleteById方法的返回值類型為void。 // Mockito已默認為返回值為void默認生了返回值,無需對此替身單元做設置 // 調用方法 this.studentService.deleteById(id); // 預測以期望的參數值調用了期望的方法 ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class); Mockito.verify(this.studentRepository).deleteById(longArgumentCaptor.capture()); Assert.assertEquals(longArgumentCaptor.getValue(), id); } ``` * 使用@MockBean注解的studentRepository,在返回值為void的方法中。Mockito已為其自動設置了調用時的返回值。 > Mockito在此對studentRepository默認執行了如下方法:Mockito.doNothing().when(this.studentRepository).deleteById(Mockito.anyLong()); # C層 相對于M層,C層還需要額外測試前臺調用此接口時的數據輸入與輸出是否符合預期。該接口返回狀態碼204,返回內容為空。 ## 單元測試 按開發規范新建用例如下: controller/StudentControllerTest.java ```java @Test public void deleteById() { // 準備替身、傳入數據及返回數據 // 向指定的地址發起請求,并斷言返回狀態碼204 // 斷言調用方法符合預期 } ``` 補充代碼: ```java @Test public void deleteById() throws Exception { // 準備替身、傳入數據及返回數據 Long id = new Random().nextLong(); // studentService.deleteById方法返回類型為void,故無需對替身進行設置 // 向指定的地址發起請求,并斷言返回狀態碼204 String url = "/Student/" + id.toString(); this.mockMvc.perform(MockMvcRequestBuilders.delete(url)) .andExpect(MockMvcResultMatchers.status().is(204)) ; // 斷言調用方法符合預期 ArgumentCaptor<Long> longArgumentCaptor = ArgumentCaptor.forClass(Long.class); Mockito.verify(this.studentService).deleteById(longArgumentCaptor.capture()); Assert.assertEquals(longArgumentCaptor.getValue(), id); } ``` * 同M層的測試相同,studentService.deleteById的返回類型為void,同樣可以省略對替身做的設置。 ## 功能代碼 按單元測試的提示補充代碼: ``` java.lang.AssertionError: Response status Expected :204 Actual :405 ``` 上述錯誤提示:找到了請求的路徑`/Stduent/xxx`但并不是`delete`請求方法。 controller/StudentController.java ```java @DeleteMapping("{id}") public void deleteById() { } ``` 繼續進行單元測試 ``` java.lang.AssertionError: Response status Expected :204 Actual :200 <Click to see difference> ``` 提示狀態碼返回了200,則修正如下: ``` @DeleteMapping("{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteById() { } ``` 繼續測試: ``` Wanted but not invoked: studentServiceImpl bean.deleteById( <Capturing argument> ); -> at com.mengyunzhi.springbootstudy.controller.StudentControllerTest.deleteById(StudentControllerTest.java:304) Actually, there were zero interactions with this mock. ``` 出現一些不常的錯誤時,最好最有效的做法是:翻譯一下,猜猜它到底在說什么。 ``` 想調用但是卻沒有調用 studentServiceImpl bean.deleteById( <Capturing argument 用于捕獲的參數> ); -> 錯誤發生在單元測試的第304行:Mockito.verify(this.studentService).deleteById(longArgumentCaptor.capture()); 實際上,在這個mock(指studentServiceImpl bean)就沒有產生影響 (沒有被調用過) ``` 由于一些專用的單詞在上學期間并沒有接觸過,所以剛開始猜可能猜的不著邊際。不過沒關系,猜多了看到這個單詞的時候多了,結合看到這些單詞的情景,慢慢就猜準了。而這也應該是在英語學習中應該有境界。接受過基礎的教育的我們,英文的學習年限最少也有有6年,而且還是在記憶及學習能力都較優秀的青少年時代,但學習的成果好像還不如1-6周歲時對母語的學習。筆者猜想這是由于語言的學習天生需要"環境"的特性而決定的。 翻譯后發現報錯信息大概是說:這個(this.studentService)上的deleteById沒有如預期被調用。修正如下: controller/StudentController.java ```java @DeleteMapping("{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteById(Long id) { this.studentService.deleteById(id); } ``` 繼續測試: ``` java.lang.AssertionError: Expected :null Actual :6076760375028858591 <Click to see difference> ... at com.mengyunzhi.springbootstudy.controller.StudentControllerTest.deleteById(StudentControllerTest.java:305) ``` 提示305行發生錯誤,期望接收的參數是6076760375028858591這個隨機數,但卻接收到了null,繼續修正: controller/StudentController.java ```java @DeleteMapping("{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteById(@PathVariable Long id) { this.studentService.deleteById(id); } ``` 繼續測試后單元測試通過,功能完成。 在開發過程中往往會一次性的寫完自己能想到的功能性代碼,然后再結合測試來補充一些遺忘的功能性代碼或者修正一些單元測試及功能性代碼的錯誤。無論是寫單元測試還是功能性的代碼都是對業務邏輯的一次實現,對同一業務邏輯的兩次實現有效的地降低了一些在書寫時的低級錯誤。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.8.3](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.8.3) | - | | mockito void methods | [https://www.baeldung.com/mockito-void-methods](https://www.baeldung.com/mockito-void-methods) | 10 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看