在使用 Spring boot 的過程當中發現 Spring Boot 的版本更迭很是的快,而不一樣的版本的不少語法和支持都有必定的區別,當遇到一個問題去 stackoverflow 搜索的時候常常會發現不一樣版本的解決方案,弄得我非常苦惱。(真是找到了用 npm 的感受,每次升級包都會出問題。每到這個時候就念到了 rails 的好,一個成熟的、穩定、合理的生態體系是多麼的重要!)。因此在這裏我明確的在標題裏提到了我所使用的版本 1.5.3
。java
Spring 官網提供了太多的 Getting Started 好比這個或者是 Hello World 的示例。這些示例真的是太太太簡單了,徹底沒辦法做爲學習的材料(再次強調,能不能看看人家 Rails 官方的 Guide 呀),而去其餘地方搜索的內容又多是早期版本(由於版本更迭快呀)的內容,把不一樣的版本中的衆多實踐方式進行比較並拼湊在一塊兒也非常浪費時間,因此我在這裏就在官方的基礎上座一個稍微多一點的樣例,但願這裏的內容能夠做爲實際開發中的參考。git
注意 這裏所展現的測試的例子是對 RESTful API 的測試,在先後端分離,構建微服務的今天,咱們在 Spring MVC 中作模板渲染的狀況愈來愈少了了,咱們主要處理的是 JSON 數據:咱們的輸入不是傳統的表單數據而是 JSON,咱們的輸出再也不是 HTML 而是 JSON。github
測試的重要性是老生常談了,但實際上並非全部的團隊都會在寫代碼的同時寫測試,在看到大量的 Spring Boot 的文章和代碼的時候竟然很難找到一個完整的、包含着測試的項目,真是恐怖。不過作了一些 search 以後我發現 Spring Boot 目前的測試真的是很是的簡單,和 Jersey 比的話那真是好的太多了。一個基本的、純粹的 Spring MVC 的測試長以下的樣子,這裏涉及多個例子,我會一點點作介紹。web
@RunWith(SpringRunner.class) // [1] public class UsersApiTest { private UserRepository userRepository; @Before public void setUp() throws Exception { userRepository = mock(UserRepository.class); MockMvc mockMvc = MockMvcBuilders .standaloneSetup(new UsersApi(userRepository)) .setControllerAdvice(new CustomizeExceptionHandler()) .build(); // [2] RestAssuredMockMvc.mockMvc(mockMvc); // [3] } @Test public void should_get_empty_user_lists_success() throws Exception { // [4] given(). when(). get("/users"). then(). statusCode(200); } @Test public void should_create_user_success() throws Exception { Map<String, Object> createUserParameter = new HashMap<String, Object>() {{ put("username", "aisensiy"); }}; given() .contentType("application/json") .body(createUserParameter) .when().post("/users") .then().statusCode(201); verify(userRepository).save(any()); } @Test public void should_get_400_error_message_with_wrong_parameter_when_create_user() throws Exception { Map<String, Object> wrongParameter = new HashMap<String, Object>() {{ put("name", "aisensiy"); }}; given() .contentType("application/json") .body(wrongParameter) .when().post("/users") .then().statusCode(400) .body("fieldErrors[0].field", equalTo("username")) // [5] .body("fieldErrors.size()", equalTo(1)); } @Test public void should_get_one_user_success() throws Exception { User user = new User(UUID.randomUUID().toString(), "aisensiy"); when(userRepository.findById(eq(user.getId()))) .thenReturn(Optional.of(user)); given() .standaloneSetup(new UserApi(userRepository)) .when().get("/users/{userId}", user.getId()) // [6] .then().statusCode(200) .body("id", equalTo(user.getId())) .body("username", equalTo(user.getUsername())) .body("links.self", endsWith("/users/" + user.getId())); } }
以上的代碼包含了四個測試用例,測試內容以下:spring
GET /users
獲取用戶列表POST /users
用合法的參數建立一個用戶,返回建立成功POST /users
用非法的參數建立一個用戶,返回參數錯誤信息GET /users/{userId}
獲取單個用戶的信息下面我按照對代碼中標註的點一個個作解釋:npm
SpringJUnit4ClassRunner
被替換爲更容易閱讀的 SpringRunner
,在 stackoverflow 中會找到大量的 SpringJUnit4ClassRunner
對我這種剛接觸的人來講真是帶來了不少的困惑。另外,咱們在這裏並無使用一個 SpringBootTest
的註解,SpringBootTest 是隻有須要一個比較完整的 Spring Boot 環境的時候(好比須要作集成測試,啓動 EmbeddedWebApplicationContext
的時候)須要。而咱們這裏僅僅經過單元測試就能夠完成任務了,這樣的好處是能夠大大提高測試的速度。MockMvcBuilders
是 Spring MVC 提供的一個 mock 環境,使咱們能夠不啓動 HTTP server 就能進行測試。這裏咱們經過 standaloneSetup
的方法建立咱們要測試的 UsersApi
而且經過 setControllerAdvice
添加錯誤處理的機制。有關 ControllerAdvice
作異常處理的內容咱們會在後面的文章中介紹。build.gradle
引入了 rest assured 的兩個包用於 json 的測試,咱們經過這個語句將所建立的 mock mvc 提供給 rest assured。status code
,實際的項目中可能須要作更詳細的 json 內容的測試body("fieldErrors[0].field", equalTo("username"))
這種直接讀取 json path 的測試方式相對將 json 轉化成 map 再一點點的讀取字段來講真是方便的太多,有關這種測試的其餘內容詳見 rest assured 官方文檔PathVariable
相似大多數狀況下,經過 standaloneSetup
的方式就能夠對 Controller
進行有效的單元測試了,固然 MockMvcBuilders
也能夠引入外部的 ControllerAdvice
對錯誤處理進行測試。加上 rest assured 測試 json api 真是簡單了太多了。不過這裏並無覆蓋 filter 的測試,後面的有關安全的文章會補上。json
最後附上項目所使用的 build.gradle
,完整的項目內容能夠在 Github 找到。後端
// build.gradle buildscript { ext { springBootVersion = '1.5.3.RELEASE' } repositories { mavenCentral() } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") } } apply plugin: 'java' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 repositories { mavenCentral() } dependencies { compile('org.flywaydb:flyway-core') compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.0') compile("org.springframework.boot:spring-boot-starter-hateoas") compile('org.springframework.boot:spring-boot-starter-web') runtime('com.h2database:h2') compileOnly('org.projectlombok:lombok') testCompile('org.springframework.boot:spring-boot-starter-test') testCompile('org.mybatis.spring.boot:mybatis-spring-boot-starter-test:1.3.0') testCompile 'io.rest-assured:rest-assured:3.0.2' testCompile 'io.rest-assured:spring-mock-mvc:3.0.2' }
更多信息見 aisensiy.github.ioapi