本文首發於我的網站:Spring Boot項目中使用Mockitojava
Spring Boot能夠和大部分流行的測試框架協同工做:經過Spring JUnit建立單元測試;生成測試數據初始化數據庫用於測試;Spring Boot能夠跟BDD(Behavier Driven Development)工具、Cucumber和Spock協同工做,對應用程序進行測試。git
進行軟件開發的時候,咱們會寫不少代碼,不過,再過六個月(甚至一年以上)你知道本身的代碼怎麼運做麼?經過測試(單元測試、集成測試、接口測試)能夠保證系統的可維護性,當咱們修改了某些代碼時,經過迴歸測試能夠檢查是否引入了新的bug。總得來講,測試讓系統再也不是一個黑盒子,讓開發人員確認系統可用。程序員
在web應用程序中,對Controller層的測試通常有兩種方法:(1)發送http請求;(2)模擬http請求對象。第一種方法須要配置迴歸環境,經過修改代碼統計的策略來計算覆蓋率;第二種方法是比較正規的思路,可是在我目前經歷過的項目中用得很少,今天總結下如何用Mock對象測試Controller層的代碼。github
在以前的幾篇文章中,咱們都使用bookpub這個應用程序做爲例子,今天也不例外,準備測試它提供的RESTful接口是否能返回正確的響應數據。這種測試不一樣於單元測試,須要爲之初始化完整的應用程序上下文、全部的spring bean都織入以及數據庫中須要有測試數據,通常來講這種測試稱之爲集成測試或者接口測試。web
經過spirng.io新建的Spring Boot項目提供了一個空的測試文件——BookPubApplicationTest.java,內容是:面試
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = BookPubApplication.class)
public class BookPubApplicationTests {
@Test
public void contextLoads() {
}
}複製代碼
<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>複製代碼
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("中文測試", book.getPublisher().getName());
}
@Test
public void webappPublisherApi() throws Exception {
//MockHttpServletRequestBuilder.accept方法是設置客戶端可識別的內容類型
//MockHttpServletRequestBuilder.contentType,設置請求頭中的Content-Type字段,表示請求體的內容類型
mockMvc.perform(get("/publishers/1")
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(content().string(containsString("中文測試")))
.andExpect(jsonPath("$.name").value("中文測試"));
}
}複製代碼
# To create test coverage reports (in target/site/cobertura)
mvn clean cobertura:cobertura test複製代碼
首先分析在BookPubApplicationTests類中用到的註解:spring
瞭解完測試類的註解,再看看測試類的內部。因爲這是Spring Boot的測試,所以咱們可經過@Autowired註解織入任何由Spring管理的對象,或者是經過@Value設置指定的環境變量的值。在如今這個測試類中,咱們定義了WebApplicationContext和BookRepository對象。數據庫
每一個測試用例用@Test註解修飾。在第一個測試用例——contextLoads()方法中,我僅僅須要確認BookRepository鏈接已經創建,而且數據庫中已經包含了對應的測試數據。json
第二個測試用例用來測試咱們提供的RESTful URL——經過ISBN查詢一本書,即「/books/{isbn}」。在這個測試用例中咱們使用TestRestTemplate對象發起RESTful請求。後端
第三個測試用例中展現瞭如何經過MockMvc對象實現跟第二個測試相似的功能。Spring測試框架提供MockMvc對象,能夠在不須要客戶端-服務端請求的狀況下進行MVC測試,徹底在服務端這邊就能夠執行Controller的請求,跟啓動了測試服務器同樣。
測試開始以前須要創建測試環境,setup方法被@Before修飾。經過MockMvcBuilders工具,使用WebApplicationContext對象做爲參數,建立一個MockMvc對象。
MockMvc對象提供一組工具函數用來執行assert判斷,都是針對web請求的判斷。這組工具的使用方式是函數的鏈式調用,容許程序員將多個測試用例連接在一塊兒,並進行多個判斷。在這個例子中咱們用到下面的一些工具函數:
@Test
public void webappPublisherApi() throws Exception {
//MockHttpServletRequestBuilder.accept方法是設置客戶端可識別的內容類型
//MockHttpServletRequestBuilder.contentType,設置請求頭中的Content-Type字段,表示請求體的內容類型
mockMvc.perform(get("/publishers/1")
.accept(MediaType.APPLICATION_JSON_UTF8))
.andExpect(status().isOk())
.andExpect(content().string(containsString("中文測試")))
.andExpect(jsonPath("$.name").value("中文測試"));
}複製代碼
***本號專一於後端技術、JVM問題排查和優化、Java面試題、我的成長和自我管理等主題,爲讀者提供一線開發者的工做和成長經驗,期待你能在這裏有所收穫。