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

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                之前的章節我們都是使用postman來進行接口測試的,本節我們來教大家使用編碼的方式來進行接口測試。 在開始測試之前我們調整一下代碼(方便測試): ![](https://img.kancloud.cn/9a/a3/9aa3642145cfab32974a3dd678f0ad95_1887x269.png) 使用Postman的測試返回結果如下: ![](https://img.kancloud.cn/bf/0c/bf0cf1288f1ad17c4bb456fb0c9b1c24_1071x650.png) ## 一、編碼實現接口測試 #### 1.1.問:為什么要寫代碼做測試?使用接口測試工具Postman很方便啊 答:因為在做系統的自動化持續集成的時候,會要求自動的做單元測試,只有所有的單元測試都跑通了,才能打包構建。比如:使用maven在打包之前將所有的測試用例執行一遍。這里重點是**自動化**,所以postman這種工具很難插入到持續集成的自動化流程中去。 #### 1.2.junit測試框架 在開始書寫測試代碼之前,我們先回顧一下JUnit常用的測試注解。在junit4和junit5中,注解的寫法有些許變化。 | junit4 | junit5 | 特點 | | --- | --- | --- | | @Test | @Test | 聲明一個測試方法 | | @BeforeClass | @BeforeAll | 在當前類的所有測試方法之前執行。注解在【靜態方法】上 | | @AfterClass | @AfterAll | 在當前類中的所有測試方法之后執行。注解在【靜態方法】上 | | @Before | @BeforeEach | 在每個測試方法之前執行。注解在【非靜態方法】上 | | @After | @AfterEach | 在每個測試方法之后執行。注解在【非靜態方法】 | | @RunWith(SpringRunner.class) | @ExtendWith(SpringExtension.class) | 類class定義上 | #### 1.3.Mockito測試框架 Mockito是GitHub上使用最廣泛的Mock框架,并與JUnit結合使用.Mockito框架可以創建和配置mock對象.使用Mockito簡化了具有外部依賴的類的測試開發。Mockito測試框架可以幫助我們模擬HTTP請求,從而達到在服務端測試目的。因為其不會真的去發送HTTP請求,而是模擬HTTP請求內容,從而節省了HTTP請求的網絡傳輸,測試速度更快。 ![](https://img.kancloud.cn/49/e6/49e69a8d69054466f98be13e22c60e12_1062x364.png) ~~~ <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> ~~~ > spring-boot-starter-test(Spring Boot 2.3.0.RELEASE)自動包含Junit 5 和Mockito框架,以下測試代碼是基于Junit5,使用Junit4的同學請自行調整代碼。 ~~~ @Slf4j public class ArticleRestControllerTest { //mock對象 private static MockMvc mockMvc; //在所有測試方法執行之前進行mock對象初始化 @BeforeAll static void setUp() { mockMvc = MockMvcBuilders.standaloneSetup(new ArticleController()).build(); } //測試方法 @Test public void saveArticle() throws Exception { String article = "{\n" + " \"id\": 1,\n" + " \"author\": \"zimug\",\n" + " \"title\": \"手摸手教你開發spring boot\",\n" + " \"content\": \"c\",\n" + " \"createTime\": \"2017-07-16 05:23:34\",\n" + " \"reader\":[{\"name\":\"zimug\",\"age\":18},{\"name\":\"kobe\",\"age\":37}]\n" + "}"; MvcResult result = mockMvc.perform( MockMvcRequestBuilders .request(HttpMethod.POST, "/rest/articles") .contentType("application/json") .content(article) ) .andExpect(MockMvcResultMatchers.status().isOk()) //HTTP:status 200 .andExpect(MockMvcResultMatchers.jsonPath("$.data.author").value("zimug")) .andExpect(MockMvcResultMatchers.jsonPath("$.data.reader[0].age").value(18)) .andDo(print()) .andReturn(); result.getResponse().setCharacterEncoding("UTF-8"); log.info(result.getResponse().getContentAsString()); } } ~~~ MockMvc對象有以下幾個基本的方法: * perform : 模擬執行一個RequestBuilder構建的HTTP請求,會執行SpringMVC的流程并映射到相應的控制器Controller執行。 * contentType:發送請求內容的序列化的格式,"application/json"表示JSON數據格式 * andExpect: 添加RequsetMatcher驗證規則,驗證控制器執行完成后結果是否正確,或者說是結果是否與我們期望(Expect)的一致。 * andDo: 添加ResultHandler結果處理器,比如調試時打印結果到控制臺 * andReturn: 最后返回相應的MvcResult,然后進行自定義驗證/進行下一步的異步處理 > 上面的整個過程,我們都沒有使用到Spring Context依賴注入、也沒有啟動tomcat web容器。整個測試的過程十分的輕量級,速度很快。 ## 二、真實servlet容器環境下的測試 上面的測試執行速度非常快,但是有一個問題:它沒有啟動servlet容器和Spring 上下文,自然也就無法實現依賴注入(不支持@Resource和@AutoWired注解)。這就導致它在從控制層到持久層全流程測試中有很大的局限性。 ![](https://img.kancloud.cn/51/16/51161d5ade8b67cc8a4d10e0740094e8_1075x296.png) 換一種寫法:看看有沒有什么區別。在測試類上面額外加上這樣兩個注解,并且mockMvc對象使用@Resource自動注入,刪掉Before注解及setUp函數。 ~~~ @AutoConfigureMockMvc @SpringBootTest @ExtendWith(SpringExtension.class) ~~~ ![](https://img.kancloud.cn/8b/19/8b191e40c66f95316a45a1f8f7b60ed4_1285x516.png) 啟動測試一下,看看和之前有沒有什么區別. ![](https://box.kancloud.cn/e6994b5795a3afddf38eb85e83932a89_1603x263.png) 看到上面這個截圖,是不是已經明白了!該測試方法真實的啟動了一個tomcat容器、以及Spring 上下文,所以我們可以進行依賴注入(@Resource)。實現的效果和使用MockMvcBuilders構建MockMVC對象的效果是一樣的,但是有一個非常明顯的缺點:每次做一個接口測試,都會真實的啟動一次servlet容器,Spring上下文加載項目里面定義的所有的Bean,導致執行過程很緩慢。 ### 2.1 @SpringBootTest 注解 是用來創建Spring的上下文ApplicationContext,保證測試在上下文環境里運行。單獨使用@SpringBootTest不會啟動servlet容器。所以**只是使用SpringBootTest 注解,不可以使用@Resource和@Autowired等注解進行bean的依賴注入**。(準確的說是可以使用,但被注解的bean為null)。 ### 2.2 @ExtendWith(@RunWith注解) * RunWith方法為我們構造了一個的Servlet容器運行運行環境,并在此環境下測試。然而為什么要構建servlet容器?因為使用了依賴注入,注入了MockMvc對象,而在上一個例子里面是我們自己new的。 * 而@AutoConfigureMockMvc注解,該注解表示mockMvc對象由spring 依賴注入構建,你只負責使用就可以了。這種寫法是為了讓測試在servlet容器環境下執行。 簡單的說:**如果你單元測試代碼使用了“依賴注入@Resource”就必須加上@ExtendWith,如果你不是手動new MockMvc對象就加上@AutoConfigureMockMvc** ### 2.3 @Transactional 該注解加在方法上可以使單元測試進行事務回滾,以保證數據庫表中沒有因測試造成的垃圾數據,因此保證單元測試可以反復執行; 但是筆者不建議這么做,使用該注解會破壞測試真實性。請參考這篇文章詳細理解: [不要在 Spring Boot 集成測試中使用 @Transactional](http://www.zimug.com/other/springboot/%e4%b8%8d%e8%a6%81%e5%9c%a8spring%e5%8d%95%e5%85%83%e6%b5%8b%e8%af%95%e4%b8%ad%e4%bd%bf%e7%94%a8-transactional%e6%b3%a8%e8%a7%a3/.html) ## 三、Mock測試 ### 3.1什么是Mock? 在面向對象程序設計中,模擬對象(英語:mock object,也譯作模仿對象)是以可控的方式模擬真實對象行為的**假的對象**。比如:對象B依賴于對象A,但是A代碼還沒寫是一個空類空方法不能用,我們來mock一個假的A來完成測試。 ### 3.2 為什么要使用Mock? ![](https://img.kancloud.cn/4e/70/4e7030c801cf14348b7e2a2287e04144_764x488.png) > 在單元測試中,模擬對象可以模擬復雜的、真實的對象的行為, 如果真實的對象無法放入單元測試中,使用模擬對象就很有幫助。 在下面的情形,可能需要使用**"模擬對象行為"**來代替真實對象: * 真實對象的行為是不確定的(例如,當前的時間或當前的溫度); * 真實對象很難搭建起來; * 真實對象的行為很難觸發(例如,網絡錯誤); * 真實對象速度很慢(例如,一個完整的數據庫,在測試之前可能需要初始化); * 真實的對象是用戶界面,或包括用戶界面在內; * 真實的對象使用了回調機制; * 真實對象可能還不存在(例如,其他程序員還為完成工作); * 真實對象可能包含不能用作測試的信息(高度保密信息等)和方法。 ### 3.3.場景實踐 我們的保存文章的Controller方法,調用ArticleService的saveArticle進行文章的保存。 ![](https://img.kancloud.cn/99/15/9915f4d54a24e5a4ab3b99e9d847aa4a_1161x209.png) 但是因為種種原因,這個接口目前沒能實現(只有接口,代碼如下)。比如說:另一個程序員暫時沒完成工作,或者是機密內容實現,不能被用于測試環境。 ~~~ public interface ArticleService { public String saveArticle(Article article); } ~~~ 但是現在接口調用方找到我了,需要進行接口驗證。怎么辦?我們就可以使用Mock的方法,先Mock一個假的ArticleService,把接口驗證完成。 ~~~ @Slf4j @AutoConfigureMockMvc @SpringBootTest @ExtendWith(SpringExtension.class) public class ArticleRestControllerTest3 { //mock對象 @Resource private MockMvc mockMvc; @MockBean private ArticleService articleService; //測試方法 @Test public void saveArticle() throws Exception { String article = "{\n" + " \"id\": 1,\n" + " \"author\": \"zimug\",\n" + " \"title\": \"手摸手教你開發spring boot\",\n" + " \"content\": \"c\",\n" + " \"createTime\": \"2017-07-16 05:23:34\",\n" + " \"reader\":[{\"name\":\"zimug\",\"age\":18},{\"name\":\"kobe\",\"age\":37}]\n" + "}"; ObjectMapper objectMapper = new ObjectMapper(); Article articleObj = objectMapper.readValue(article,Article.class); //打樁 when(articleService.saveArticle(articleObj)).thenReturn("ok"); MvcResult result = mockMvc.perform( MockMvcRequestBuilders.request(HttpMethod.POST, "/rest/articles") .contentType("application/json").content(article)) .andExpect(MockMvcResultMatchers.jsonPath("$.data").value("ok")) .andDo(print()) .andReturn(); result.getResponse().setCharacterEncoding("UTF-8"); log.info(result.getResponse().getContentAsString()); } } ~~~ ### @MockBean 可以用MockBean偽造模擬一個Service ,如上圖中的MockBean。 大家注意上文代碼中,打了一個樁 ~~~ when(articleService.saveArticle(articleObj)).thenReturn("ok"); ~~~ 也就是告訴測試用例程序,當你調用articleService.saveArticle(articleObj)方法的時候,不要去真的調用這個方法,直接返回一個結果(“ok”)就好了。 ~~~ .andExpect(MockMvcResultMatchers.jsonPath("$.data").value("ok")) ~~~ 測試用例跑通了,期望結果andExpect:ok與實際結果thenReturn("ok")一致。表示程序真正的去執行了MockBean的模擬行為,而不是調用真實對象的方法。 ## 四、輕量級測試 在ExtendWith的AutoConfigureMockMvc注解的共同作用下,啟動了SpringMVC的運行容器,并且把項目中所有的@Bean全部都注入進來。把所有的bean都注入進來是不是很臃腫?這樣會拖慢單元測試的效率。如果我只是想測試一下控制層Controller,怎么辦?或者說我只想具體到測試一下ArticleRestController,怎么辦?要把應用中所有的bean都注入么?有沒有輕量級的解決方案?一定是有的。 ~~~ @ExtendWith(SpringExtension.class) @WebMvcTest(ArticleController.class) //@SpringBootTest ~~~ #### 使用@WebMvcTest替換@SpringBootTest * @SpringBootTest注解告訴SpringBoot去尋找一個主配置類(例如帶有@SpringBootApplication的配置類),并使用它來啟動Spring應用程序上下文。SpringBootTest加載完整的應用程序并注入所有可能的bean,因此速度會很慢。 * @WebMvcTest注解主要用于controller層測試,只覆蓋應用程序的controller層,@WebMvcTest(ArticleController.class)只加載ArticleController這一個Bean用作測試。所以WebMvcTest要快得多,因為我們只加載了應用程序的一小部分。 ## 五、MockMvc更多的用法總結 ~~~ //模擬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("", "")); ~~~ 附: 踩坑情況,字符集亂碼失效的時候可以用第二種辦法,雖然是已過時的。 ![](https://img.kancloud.cn/19/36/193660eba96dc3578ac8923bf55f5b75_1185x744.png)
                  <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>

                              哎呀哎呀视频在线观看