團隊開發中,文檔尤其重要。文檔的存大,大幅的下降了團隊間的無效溝通時間,使得每位成員便可以按本身的計劃開始工做,又能夠快速的獲取到其它隊員的支持。html
優秀的程序員從善於讀文檔開始,優秀的工程師從善於寫文檔開始。
在先後臺的開發中,接口是使先後臺進行關聯的關鍵一環,接口是否合理,清晰直接影響到團隊開發的效率和團隊間配置的心情(程序員每每都是自戀的壞脾氣)。而是否可以提供隨時更新的閱讀性強的文檔,則是先後臺開發中的重要一環。java
本文由0開始,記錄一次在實際項目中使用Spring REST Docs + travis + github
自動生成API接口的實際操做過程。git
本文環境:java:1.8
+ maven:3.3
+ spring-boot:2.0.3
程序員
咱們一直堅信,官方的原文文檔確定是最好的。第一步,咱們來到了spring rest docs
的官方文檔,並參考其作如下操做:github
找到pom.xml
加入如下依賴。IDEA會自動爲咱們導入依賴的包,其它的IDE,可能須要手動導入。web
<dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-mockmvc</artifactId> <version>2.0.1.RELEASE</version> <scope>test</scope> </dependency> <build> <plugins> <plugin> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-maven-plugin</artifactId> <version>1.5.3</version> <executions> <execution> <id>generate-docs</id> <phase>prepare-package</phase> <goals> <goal>process-asciidoc</goal> </goals> <configuration> <backend>html</backend> <doctype>book</doctype> </configuration> </execution> </executions> <dependencies> <dependency> <groupId>org.springframework.restdocs</groupId> <artifactId>spring-restdocs-asciidoctor</artifactId> <version>2.0.1.RELEASE</version> </dependency> </dependencies> </plugin> </plugins> </build>
注意: dependency
須要加入到dependencies
中。若是你的pom.xml
有了build
-> plugins
,則只須要複製plugin
的部分到plugins
中。spring
如下代碼的做用以下:json
4.Add spring-restdocs-asciidoctor as a dependency of the Asciidoctor plugin. This will automatically configure the snippets attribute for use in your .adoc files to point to target/generated-snippets. It will also allow you to use the operation block macro.mvc
胡亂翻譯以下:app
可能會遇到的小坑:
- 粘貼上述代碼時,位置錯了。此時,須要按前面給的注意檢查,是否出現了嵌套錯誤。
- 提示
spring-restdocs-asciidoctor:2.0.1.RELEASE
未找到,這是因爲咱們本機的maven
庫的索引太舊引發來。查看官網咱們得知,這個包的引入時間是Apr 4, 2018
。此時,須要咱們從新下載maven
的目錄。即update maven indices
咱們在測試控制器時,會使用MockMVC
來進行模擬數據的發送,這個模擬數據正好成爲了咱們須要展示在API接口部分的樣例。
這樣作的好處很明顯:
好比,我如今有個測試的控制器是: DanWeiControllerTest
(歷史緣由,請忽略命名)。爲了能夠統一地對全部的控制器測試進行自動文檔的設置,咱們在這再新建一個抽像類ControllerTest
,並使用DanWeiControllerTest
來繼續ControllerTest
。
ControllerTest
package com.mengyunzhi.check_apply_online.controller; import org.junit.Before; import org.junit.Rule; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.restdocs.JUnitRestDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentationConfigurer; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvcBuilder; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import javax.transaction.Transactional; /** * 單元測試基類 * @author panjie * www.mengyunzhi.com 河北工業大學夢雲智開發團隊 */ @SpringBootTest @RunWith(SpringRunner.class) @Transactional @AutoConfigureMockMvc public abstract class ControllerTest { protected MockMvc mockMvc; @Autowired protected WebApplicationContext context; @Rule public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(); @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)) .build(); } }
DanWeiControllerTest
進行繼承(未加入自動文檔)
package com.mengyunzhi.check_apply_online.controller; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * 單位 * @author panjie * www.mengyunzhi.com 河北工業大學夢雲智開發團隊 */ public class DanWeiControllerTest extends ControllerTest{ @Test public void registerTest() throws Exception { String url = "/DanWei/register"; this.mockMvc.perform(MockMvcRequestBuilders .post(url) .contentType(MediaType.APPLICATION_JSON_UTF8) .content("{}")) .andExpect(MockMvcResultMatchers.status().isOk()); } }
加入自動生成文檔代碼後:
package com.mengyunzhi.check_apply_online.controller; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; /** * 單位測試 * @author panjie * www.mengyunzhi.com 河北工業大學夢雲智開發團隊 */ public class DanWeiControllerTest extends ControllerTest{ @Test public void registerTest() throws Exception { String url = "/DanWei/register"; this.mockMvc.perform(MockMvcRequestBuilders .post(url) .contentType(MediaType.APPLICATION_JSON_UTF8) .content("{}")) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcRestDocumentation.document("DanWei_register")); } }
此時,咱們執行本單元測試時,將在target/generated-snippets
中生成以下6個以.adoc
結尾的文件:
這6個文件中,記錄了不一樣請求方法的請求/返回信息。
咱們的單元測試請求越多,生成這樣的文件夾也就越多,下面,咱們共同將這些文件進行格式化,並拼接到一個html文件中。
在src/main/asciidoc/
建立一個任意名稱的.adoc
文件,好比,咱們index.adoc
。而後,使用.adoc
的include
語法,將上圖中生成的幾個測試代碼片斷包含進來。
src/main/asciidoc/index.adoc
include::{snippets}/DanWei_register/http-request.adoc[] include::{snippets}/DanWei_register/request-body.adoc[] include::{snippets}/DanWei_register/http-response.adoc[] include::{snippets}/DanWei_register/response-body.adoc[]
當咱們進行package
時,就會在target
文件夾中生成generated-docs/index.html
,點擊便可查看生成的文檔信息。
mvn package
查看html
以下:
至此,咱們便成功的使用spring rest docs
生成了html
的API接口文檔。除了基本的功能之外,咱們還能夠對傳入的字段添加說明等。具體的細節,若是有需求,能夠查看Spring
的官方文檔。
咱們按照Spring
的官方文檔進行了相應的配置;接着在單位測試時,加入了指定生成文檔的目錄代碼,併成功的生成測試的代碼片斷;而後使用AsciiDoc
語法,在index.adoc
文件中,對測試代碼片斷進行了拼接;最後,使用mvn package
在進行項目打包的同時,生成了咱們想的html
文件。
不足
但咱們仍然感受這並不夠智能化,咱們在生成代碼片斷後,還須要手動的加入對代碼版本的引入;雖然有了代碼片斷,但對片斷中的請求json
數據的解釋並不輕鬆(要參考官方文檔),最主要的是,在C層,咱們進行接收時,要麼是通常的參數,要麼是後臺的實體。咱們更但願可以對參數和實體進行更改時,測試的說明也可以同時更改。
那麼如何可以自動的實現代碼片斷的合併,而且按照參數和實體信息自動生成與後臺代碼高度吻合的API說明呢?
咱們將在下節中,給出闡述。