<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>

                按照我們總結的TDD的開發流程,開始進行后臺開發。由于我們當前為前后臺分離的架構,所以省略了原型開發,而直接由第二步功能代碼初始化做起。 由前臺的開發稍有不同的是,在后臺的開發中我們已經進行了更合理的分層,在分層開發時我們需要依據每個層具體負責的功能來編寫針對性的測試語句。 ## 初始化 controller/KlassController.java ``` @DeleteMapping("{id}") @ResponseStatus(HttpStatus.NO_CONTENT) public void delete(@PathVariable Long id) { klassService.deleteById(id); } ``` service/KlassService.java ``` /** * 刪除 * * @param id 班級ID */ void deleteById(Long id); ``` service/KlassServiceImpl.java ``` @Override public void deleteById(Long id) { } ``` ## M層 在初始化中,我們分別建立了KlassController以及KlassServiceImpl。而KlassController依賴于KlassServiceImpl,所以在開發中我們一般會優先開發更底層的KlassServiceImpl。 ### 單元測試 同第一次測試KlassController的方法一樣,我們找到 KlassServiceImpl然后借助于idea來快速的生成測試文件KlassServiceImplTest.java ![](https://img.kancloud.cn/52/5f/525ff145e3663b391862d790452980a8_484x200.png) 我們繼續添加delete測試方法后如下: ``` package com.mengyunzhi.springBootStudy.service; import org.junit.Test; public class KlassServiceImplTest { @Test public void deleteById() { } } ``` 初始化: ``` package com.mengyunzhi.springBootStudy.service; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; @SpringBootTest @RunWith(SpringRunner.class) public class KlassServiceImplTest { @Autowired KlassService klassService; /** * 1. 新建一個班級 * 2. 刪除這個班級 * 3. 再次查詢該班級 * 4. 斷言該班級不存在 */ @Test public void deleteById() { } } ``` 依據注釋我們開始編寫測試功能,由于數據的增改查刪需要數據倉庫的協助,所以我們在此需要自動裝入KlassRepository: ``` import org.assertj.core.api.Assertions; ? @Autowired KlassRepository klassRepository; /** * 1. 新建一個班級 * 2. 刪除這個班級 * 3. 再次查詢該班級 * 4. 斷言該班級不存在 */ @Test public void deleteById() { Klass klass = new Klass(); klass.setName("測試班級"); klassRepository.save(klass); klassService.delete(klass.getId()); Optional<Klass>? klassOptional = klassRepository.findById(klass.getId()); Assertions?.assertThat(klassOptional.isPresent()?).isFalse(); ? } ``` * ? AssertJ是一款優秀的單元測試工具,被廣泛的用于java的單元測試中。 * ? Optional是一個可以裝入其它對象的容器 * ? 該容器中的isPresent()用于判斷容器中當前是否裝入了對象。 * ? 斷言:此時容器中未裝入任何對象,即`klassRepository.findById(klass.getId())`未查詢出對應的班級。 啟動單元測試后結果如下: ``` org.junit.ComparisonFailure: Expected :false Actual :true <Click to see difference> ... at com.mengyunzhi.springBootStudy.service.KlassServiceImplTest.delete(KlassServiceImplTest.java:36) ``` 提示我們在KlassServiceImplTest.java的36的斷言是失敗的。我們斷言是`false`,但實際的值是`true`。 ### 功能代碼 使用JPA進行刪除操作時異常簡單: KlassServiceImpl ``` @Override public void deleteById(Long id) { this.klassRepository.deleteById(id);? } ``` * ? 直接調用deleteById()即可 #### 再測試 ![](https://img.kancloud.cn/a4/83/a4834a48130b7ccbb97a72c3ff2aed09_702x104.png) 單元測試通過,說明功能符合預期。 ## C層 前面我們描述過,C層的功能主要為:數據輸入、數據轉發以及數據輸出。而邏輯實現屬于M層的功能,所以C層并不需要對由M層實現的邏輯實現做斷言。 ### 單元測試 以當前刪除為例,C層只要保證調用了M層的deleteById即為成功。而至于調用M層的deleteById后是否真正的發生了刪除操作、刪除操作是否成功,C層不負責也不關心。因此,在測試中我們也會像前臺一樣MOCK一個M層出來專門供測試使用。則按上述的原則,初始化如下: KlassControllerTest ``` /** * 刪除班級測試,測試點: * 1. 特定URL的DELETE請求是否正常(輸入) * 2. 是否返回了204狀態碼(輸出) * 3. 是否成功的進行了數據轉發(數據轉發) * @throws Exception */ @Test public void delete() throws Exception { } ``` 接下來我們添加一個模擬KlassService的對象 ``` import org.mockito.Mockito; @MockBean ? KlassService klassService; ``` * ? 注入一個實現了所有的KlassService接口規定功能的對象,并把該對象起名為klassService。 * ? 該對象的作用域為整個測試。此對象也將用于KlassController中注入KlassService。 使用 **@Autowired** 來裝配我們自己編寫的基于`KlassServiceImp`實現類創建的對象;使用 **@MockBean** 來裝入由Mockito自動根據接口來生成的對象。 ***** **接口**是一種規范、一種協議,現實生活中它是一種描述,用于對接兩種不同的產品、事物、物體、數據等。比如我們USB接口、3.5mm耳機接口,再比如因特網協議(IP),傳輸控制協議(TCP);而**類**則是藍圖、是模板,比如汽車生產圖紙、建筑圖紙等;而**對象**則指具體某些功能和屬性能的能動體,比如某輛汽車、比如某幢高樓、比如某個U盤。 正是由于我們在KlassController的聲明方法為:我需要一個有KlassService規定功能是對象,才會使得我們可以:① 在運行環境中,由Spring為其注入了基于KlassServiceImpl實例化的對象來滿足其要求;② 在測試環境中,由Mockito為求注入了一個實現了KlassService規定所有的功能的對象來滿足其要求。 > 我們把這種在編碼過程中指定**功能要求**而不是直接引用某個**對象**的方法稱為**面向接口**開發。 ***** ### 完成測試代碼 ``` @Test public void delete() throws Exception { String url = "/Klass/1"; MockHttpServletRequestBuilder delete = MockMvcRequestBuilders.delete(url); ? this.mockMvc.perform(delete) .andExpect(MockMvcResultMatchers.status().is(204)); ? Mockito.verify(klassService).deleteById(1L); ? } ``` * ? 構建一個delete請求,請求地址為/Klass/1 (輸入) * ? 模擬發起請求,斷言返回的狀態碼為204(輸出) * ? 斷言執行了klassService中的deleteById方法,且傳入的參數為1(數據轉發) #### 測試 ![](https://img.kancloud.cn/7f/f0/7ff0a13a09d6667680b030cb4e3a0465_460x82.png) 測試通過,說明我們在C層初始化時的代碼足夠的健壯。對輸入、輸出、數據轉發都做了正確的處理。 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.6.2](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.6.2) | - | | Mockito 專注于在測試中模擬服務 | [https://site.mockito.org/](https://site.mockito.org/) | - | | AssertJ 專注于測試斷言 | [https://assertj.github.io/doc/](https://assertj.github.io/doc/) | - |
                  <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>

                              哎呀哎呀视频在线观看