您好,登錄后才能下訂單哦!
Spring Boot可以和大部分流行的測(cè)試框架協(xié)同工作:通過(guò)Spring JUnit創(chuàng)建單元測(cè)試;生成測(cè)試數(shù)據(jù)初始化數(shù)據(jù)庫(kù)用于測(cè)試;Spring Boot可以跟BDD(Behavier Driven Development)工具、Cucumber和Spock協(xié)同工作,對(duì)應(yīng)用程序進(jìn)行測(cè)試。
進(jìn)行軟件開發(fā)的時(shí)候,我們會(huì)寫很多代碼,不過(guò),再過(guò)六個(gè)月(甚至一年以上)你知道自己的代碼怎么運(yùn)作么?通過(guò)測(cè)試(單元測(cè)試、集成測(cè)試、接口測(cè)試)可以保證系統(tǒng)的可維護(hù)性,當(dāng)我們修改了某些代碼時(shí),通過(guò)回歸測(cè)試可以檢查是否引入了新的bug。總得來(lái)說(shuō),測(cè)試讓系統(tǒng)不再是一個(gè)黑盒子,讓開發(fā)人員確認(rèn)系統(tǒng)可用。
在web應(yīng)用程序中,對(duì)Controller層的測(cè)試一般有兩種方法:(1)發(fā)送http請(qǐng)求;(2)模擬http請(qǐng)求對(duì)象。第一種方法需要配置回歸環(huán)境,通過(guò)修改代碼統(tǒng)計(jì)的策略來(lái)計(jì)算覆蓋率;第二種方法是比較正規(guī)的思路,但是在我目前經(jīng)歷過(guò)的項(xiàng)目中用得不多,今天總結(jié)下如何用Mock對(duì)象測(cè)試Controller層的代碼。
在之前的幾篇文章中,我們都使用bookpub這個(gè)應(yīng)用程序作為例子,今天也不例外,準(zhǔn)備測(cè)試它提供的RESTful接口是否能返回正確的響應(yīng)數(shù)據(jù)。這種測(cè)試不同于單元測(cè)試,需要為之初始化完整的應(yīng)用程序上下文、所有的spring bean都織入以及數(shù)據(jù)庫(kù)中需要有測(cè)試數(shù)據(jù),一般來(lái)說(shuō)這種測(cè)試稱之為集成測(cè)試或者接口測(cè)試。
實(shí)戰(zhàn)
通過(guò)spirng.io新建的Spring Boot項(xiàng)目提供了一個(gè)空的測(cè)試文件——BookPubApplicationTest.java,內(nèi)容是:
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) public class BookPubApplicationTests { @Test public void contextLoads() { } }
在pom文件中增加spring-boot-starter-test依賴,添加jsonPath依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.jayway.jsonpath</groupId> <artifactId>json-path</artifactId> </dependency>
在BookPubApplicationTest中添加測(cè)試用例
package com.test.bookpub; import com.test.bookpub.domain.Book; import com.test.bookpub.repository.BookRepository; import org.junit.Before;import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.WebIntegrationTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.client.RestTemplate; import org.springframework.web.context.WebApplicationContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(classes = BookPubApplication.class) @WebIntegrationTest("server.port:0") public class BookPubApplicationTests { @Autowired private WebApplicationContext context; @Autowired private BookRepository bookRepository; @Value("${local.server.port}") private int port; private MockMvc mockMvc; private RestTemplate restTemplate = new TestRestTemplate(); @Before public void setupMockMvc() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test public void contextLoads() { assertEquals(1, bookRepository.count()); } @Test public void webappBookIsbnApi() { Book book = restTemplate.getForObject("http://localhost:" + port +"/books/9876-5432-1111", Book.class); assertNotNull(book); assertEquals("中文測(cè)試", book.getPublisher().getName()); } @Test public void webappPublisherApi() throws Exception { //MockHttpServletRequestBuilder.accept方法是設(shè)置客戶端可識(shí)別的內(nèi)容類型 //MockHttpServletRequestBuilder.contentType,設(shè)置請(qǐng)求頭中的Content-Type字段,表示請(qǐng)求體的內(nèi)容類型 mockMvc.perform(get("/publishers/1") .accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(containsString("中文測(cè)試"))) .andExpect(jsonPath("$.name").value("中文測(cè)試")); } }
spring boot項(xiàng)目的代碼覆蓋率
使用cobertura,參考項(xiàng)目的github地址:spring boot template
# To create test coverage reports (in target/site/cobertura) mvn clean cobertura:cobertura test
分析
首先分析在BookPubApplicationTests類中用到的注解:
了解完測(cè)試類的注解,再看看測(cè)試類的內(nèi)部。由于這是Spring Boot的測(cè)試,因此我們可通過(guò)@Autowired注解織入任何由Spring管理的對(duì)象,或者是通過(guò)@Value設(shè)置指定的環(huán)境變量的值。在現(xiàn)在這個(gè)測(cè)試類中,我們定義了WebApplicationContext和BookRepository對(duì)象。
每個(gè)測(cè)試用例用@Test注解修飾。在第一個(gè)測(cè)試用例——contextLoads()方法中,我僅僅需要確認(rèn)BookRepository連接已經(jīng)建立,并且數(shù)據(jù)庫(kù)中已經(jīng)包含了對(duì)應(yīng)的測(cè)試數(shù)據(jù)。
第二個(gè)測(cè)試用例用來(lái)測(cè)試我們提供的RESTful URL——通過(guò)ISBN查詢一本書,即“/books/{isbn}”。在這個(gè)測(cè)試用例中我們使用TestRestTemplate對(duì)象發(fā)起RESTful請(qǐng)求。
第三個(gè)測(cè)試用例中展示了如何通過(guò)MockMvc對(duì)象實(shí)現(xiàn)跟第二個(gè)測(cè)試類似的功能。Spring測(cè)試框架提供MockMvc對(duì)象,可以在不需要客戶端-服務(wù)端請(qǐng)求的情況下進(jìn)行MVC測(cè)試,完全在服務(wù)端這邊就可以執(zhí)行Controller的請(qǐng)求,跟啟動(dòng)了測(cè)試服務(wù)器一樣。
測(cè)試開始之前需要建立測(cè)試環(huán)境,setup方法被@Before修飾。通過(guò)MockMvcBuilders工具,使用WebApplicationContext對(duì)象作為參數(shù),創(chuàng)建一個(gè)MockMvc對(duì)象。
MockMvc對(duì)象提供一組工具函數(shù)用來(lái)執(zhí)行assert判斷,都是針對(duì)web請(qǐng)求的判斷。這組工具的使用方式是函數(shù)的鏈?zhǔn)秸{(diào)用,允許程序員將多個(gè)測(cè)試用例鏈接在一起,并進(jìn)行多個(gè)判斷。在這個(gè)例子中我們用到下面的一些工具函數(shù):
一個(gè)字符亂碼問題
@Test public void webappPublisherApi() throws Exception { //MockHttpServletRequestBuilder.accept方法是設(shè)置客戶端可識(shí)別的內(nèi)容類型 //MockHttpServletRequestBuilder.contentType,設(shè)置請(qǐng)求頭中的Content-Type字段,表示請(qǐng)求體的內(nèi)容類型 mockMvc.perform(get("/publishers/1") .accept(MediaType.APPLICATION_JSON_UTF8)) .andExpect(status().isOk()) .andExpect(content().string(containsString("中文測(cè)試"))) .andExpect(jsonPath("$.name").value("中文測(cè)試")); }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。