RestDocumentationResultActions->RestDocumentationResultHandler

Previously documentation was handled by using a ResultActions
implementation that had custom methods added to it.

Now documentation is handled using a ResultHandler. This has a few
advantages:

- Fit better with the MockMvc programming model. This is similar to
  andDo(print())
- Support for using alwaysDo
This commit is contained in:
Rob Winch
2014-11-17 12:28:11 -06:00
parent e98658266c
commit ff7119ec54
7 changed files with 297 additions and 333 deletions

View File

@@ -19,8 +19,8 @@ package com.example.notes;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
import static org.springframework.restdocs.core.RestDocumentation.document; import static org.springframework.restdocs.core.RestDocumentation.document;
import static org.springframework.restdocs.core.RestDocumentation.linkWithRel;
import static org.springframework.restdocs.core.RestDocumentation.halLinks; import static org.springframework.restdocs.core.RestDocumentation.halLinks;
import static org.springframework.restdocs.core.RestDocumentation.linkWithRel;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -76,34 +76,33 @@ public class ApiDocumentation {
@Test @Test
public void errorExample() throws Exception { public void errorExample() throws Exception {
document( this.mockMvc
"error-example", .perform(get("/error")
this.mockMvc .requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
.perform(get("/error") .requestAttr(RequestDispatcher.ERROR_REQUEST_URI,
.requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400) "/notes")
.requestAttr(RequestDispatcher.ERROR_REQUEST_URI, .requestAttr(RequestDispatcher.ERROR_MESSAGE,
"/notes") "The tag 'http://localhost:8080/tags/123' does not exist"))
.requestAttr(RequestDispatcher.ERROR_MESSAGE,
"The tag 'http://localhost:8080/tags/123' does not exist")))
.andDo(print()).andExpect(status().isBadRequest()) .andDo(print()).andExpect(status().isBadRequest())
.andExpect(jsonPath("error", is("Bad Request"))) .andExpect(jsonPath("error", is("Bad Request")))
.andExpect(jsonPath("timestamp", is(notNullValue()))) .andExpect(jsonPath("timestamp", is(notNullValue())))
.andExpect(jsonPath("status", is(400))) .andExpect(jsonPath("status", is(400)))
.andExpect(jsonPath("path", is(notNullValue()))); .andExpect(jsonPath("path", is(notNullValue())))
.andDo(document("error-example"));
} }
@Test @Test
public void indexExample() throws Exception { public void indexExample() throws Exception {
document("index-example", this.mockMvc.perform(get("/"))
this.mockMvc.perform(get("/")).andExpect(status().isOk())) .andExpect(status().isOk())
.andDocumentLinks( .andDo(document("index-example").withLinks(halLinks(),
halLinks(),
linkWithRel("notes").description( linkWithRel("notes").description(
"The <<resources-notes,Notes resource>>"), "The <<resources-notes,Notes resource>>"),
linkWithRel("tags").description( linkWithRel("tags").description(
"The <<resources-tags,Tags resource>>"), "The <<resources-tags,Tags resource>>"),
linkWithRel("profile").description( linkWithRel("profile").description(
"The ALPS profile for the service")); "The ALPS profile for the service")));
} }
@Test @Test
@@ -116,8 +115,9 @@ public class ApiDocumentation {
"http://stateless.co/hal_specification.html"); "http://stateless.co/hal_specification.html");
createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/"); createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/");
document("notes-list-example", this.mockMvc.perform(get("/notes"))).andExpect( this.mockMvc.perform(get("/notes"))
status().isOk()); .andExpect(status().isOk())
.andDo(document("notes-list-example"));
} }
@Test @Test
@@ -137,12 +137,11 @@ public class ApiDocumentation {
note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html"); note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html");
note.put("tags", Arrays.asList(tagLocation)); note.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"notes-create-example", post("/notes").contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(note))).andExpect(
post("/notes").contentType(MediaTypes.HAL_JSON).content( status().isCreated())
this.objectMapper.writeValueAsString(note))).andExpect( .andDo(document("notes-create-example"));
status().isCreated()));
} }
@Test @Test
@@ -169,17 +168,17 @@ public class ApiDocumentation {
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated()).andReturn().getResponse()
.getHeader("Location"); .getHeader("Location");
document("note-get-example", this.mockMvc.perform(get(noteLocation))) this.mockMvc.perform(get(noteLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("title", is(note.get("title")))) .andExpect(jsonPath("title", is(note.get("title"))))
.andExpect(jsonPath("body", is(note.get("body")))) .andExpect(jsonPath("body", is(note.get("body"))))
.andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("_links.self.href", is(noteLocation)))
.andExpect(jsonPath("_links.tags", is(notNullValue()))) .andExpect(jsonPath("_links.tags", is(notNullValue())))
.andDocumentLinks( .andDo(document("note-get-example")
halLinks(), .withLinks(halLinks(),
linkWithRel("self").description("This <<resources-note,note>>"), linkWithRel("self").description("This <<resources-note,note>>"),
linkWithRel("tags").description( linkWithRel("tags").description(
"This note's <<resources-note-tags,tags>>")); "This note's <<resources-note-tags,tags>>")));
} }
@@ -192,8 +191,9 @@ public class ApiDocumentation {
createTag("Hypermedia"); createTag("Hypermedia");
createTag("HTTP"); createTag("HTTP");
document("tags-list-example", this.mockMvc.perform(get("/tags"))).andExpect( this.mockMvc.perform(get("/tags"))
status().isOk()); .andExpect(status().isOk())
.andDo(document("tags-list-example"));
} }
@Test @Test
@@ -201,12 +201,11 @@ public class ApiDocumentation {
Map<String, String> tag = new HashMap<String, String>(); Map<String, String> tag = new HashMap<String, String>();
tag.put("name", "REST"); tag.put("name", "REST");
document( this.mockMvc.perform(
"tags-create-example", post("/tags").contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(tag))).andExpect(
post("/tags").contentType(MediaTypes.HAL_JSON).content( status().isCreated())
this.objectMapper.writeValueAsString(tag))).andExpect( .andDo(document("tags-create-example"));
status().isCreated()));
} }
@Test @Test
@@ -241,12 +240,11 @@ public class ApiDocumentation {
Map<String, Object> noteUpdate = new HashMap<String, Object>(); Map<String, Object> noteUpdate = new HashMap<String, Object>();
noteUpdate.put("tags", Arrays.asList(tagLocation)); noteUpdate.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"note-update-example", patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(noteUpdate)))
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
this.objectMapper.writeValueAsString(noteUpdate))) .andDo(document("note-update-example"));
.andExpect(status().isNoContent()));
} }
@Test @Test
@@ -261,16 +259,14 @@ public class ApiDocumentation {
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated()).andReturn().getResponse()
.getHeader("Location"); .getHeader("Location");
document("tag-get-example", this.mockMvc.perform(get(tagLocation))) this.mockMvc.perform(get(tagLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("name", is(tag.get("name")))) .andExpect(jsonPath("name", is(tag.get("name"))))
.andDocumentLinks( .andDo(document("tag-get-example").withLinks(halLinks(),
halLinks(),
linkWithRel("self").description("This <<resources-tag,tag>>"), linkWithRel("self").description("This <<resources-tag,tag>>"),
linkWithRel("notes") linkWithRel("notes")
.description( .description(
"The <<resources-tagged-notes,notes>> that have this tag")); "The <<resources-tagged-notes,notes>> that have this tag")));
} }
@Test @Test
@@ -288,12 +284,11 @@ public class ApiDocumentation {
Map<String, Object> tagUpdate = new HashMap<String, Object>(); Map<String, Object> tagUpdate = new HashMap<String, Object>();
tagUpdate.put("name", "RESTful"); tagUpdate.put("name", "RESTful");
document( this.mockMvc.perform(
"tag-update-example", patch(tagLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(tagUpdate)))
patch(tagLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
this.objectMapper.writeValueAsString(tagUpdate))) .andDo(document("tag-update-example"));
.andExpect(status().isNoContent()));
} }
private void createNote(String title, String body) { private void createNote(String title, String body) {

View File

@@ -71,12 +71,11 @@ public class GettingStartedDocumentation {
@Test @Test
public void index() throws Exception { public void index() throws Exception {
document( this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON))
"index", .andExpect(status().isOk())
this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)) .andExpect(jsonPath("_links.notes", is(notNullValue())))
.andExpect(status().isOk()) .andExpect(jsonPath("_links.tags", is(notNullValue())))
.andExpect(jsonPath("_links.notes", is(notNullValue()))) .andDo(document("index"));
.andExpect(jsonPath("_links.tags", is(notNullValue()))));
} }
@Test @Test
@@ -101,49 +100,47 @@ public class GettingStartedDocumentation {
note.put("title", "Note creation with cURL"); note.put("title", "Note creation with cURL");
note.put("body", "An example of how to create a note using cURL"); note.put("body", "An example of how to create a note using cURL");
String noteLocation = document( String noteLocation = this.mockMvc
"create-note", .perform(
this.mockMvc post("/notes").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(note)))
post("/notes").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
objectMapper.writeValueAsString(note))) .andExpect(header().string("Location", notNullValue()))
.andExpect(status().isCreated()) .andDo(document("create-note"))
.andExpect(header().string("Location", notNullValue())))
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
return noteLocation; return noteLocation;
} }
void getNote(String noteLocation) throws Exception { void getNote(String noteLocation) throws Exception {
document( this.mockMvc.perform(get(noteLocation))
"get-note", .andExpect(status().isOk())
this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue())))
.andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue())))
.andExpect(jsonPath("body", is(notNullValue()))) .andExpect(jsonPath("_links.tags", is(notNullValue())))
.andExpect(jsonPath("_links.tags", is(notNullValue())))); .andDo(document("get-note"));
} }
String createTag() throws Exception, JsonProcessingException { String createTag() throws Exception, JsonProcessingException {
Map<String, String> tag = new HashMap<String, String>(); Map<String, String> tag = new HashMap<String, String>();
tag.put("name", "getting-started"); tag.put("name", "getting-started");
String tagLocation = document( String tagLocation = this.mockMvc
"create-tag", .perform(
this.mockMvc post("/tags").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(tag)))
post("/tags").contentType(MediaTypes.HAL_JSON).content(
objectMapper.writeValueAsString(tag)))
.andExpect(status().isCreated()) .andExpect(status().isCreated())
.andExpect(header().string("Location", notNullValue()))) .andExpect(header().string("Location", notNullValue()))
.andReturn().getResponse().getHeader("Location"); .andDo(document("create-tag"))
.andReturn().getResponse().getHeader("Location");
return tagLocation; return tagLocation;
} }
void getTag(String tagLocation) throws Exception { void getTag(String tagLocation) throws Exception {
document( this.mockMvc.perform(get(tagLocation))
"get-tag", .andExpect(status().isOk())
this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("name", is(notNullValue())))
.andExpect(jsonPath("name", is(notNullValue()))) .andExpect(jsonPath("_links.notes", is(notNullValue())))
.andExpect(jsonPath("_links.notes", is(notNullValue())))); .andDo(document("get-tag"));
} }
String createTaggedNote(String tag) throws Exception { String createTaggedNote(String tag) throws Exception {
@@ -152,59 +149,61 @@ public class GettingStartedDocumentation {
note.put("body", "An example of how to create a tagged note using cURL"); note.put("body", "An example of how to create a tagged note using cURL");
note.put("tags", Arrays.asList(tag)); note.put("tags", Arrays.asList(tag));
String noteLocation = document( String noteLocation = this.mockMvc
"create-tagged-note", .perform(
this.mockMvc post("/notes").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(note)))
post("/notes").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
objectMapper.writeValueAsString(note))) .andExpect(header().string("Location", notNullValue()))
.andExpect(status().isCreated()) .andDo(document("create-tagged-note"))
.andExpect(header().string("Location", notNullValue())))
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
return noteLocation; return noteLocation;
} }
void getTaggedNote(String tagLocation) throws Exception { void getTaggedNote(String tagLocation) throws Exception {
document( this.mockMvc.perform(get(tagLocation))
"get-tagged-note", .andExpect(status().isOk())
this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue())))
.andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue())))
.andExpect(jsonPath("body", is(notNullValue()))) .andExpect(jsonPath("_links.tags", is(notNullValue())))
.andExpect(jsonPath("_links.tags", is(notNullValue())))); .andDo(document("get-tagged-note"));
} }
void getTags(String taggedNoteLocation) throws Exception { void getTags(String taggedNoteLocation) throws Exception {
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
.andReturn(), "tags"); .andReturn(), "tags");
document("get-tags",
this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) this.mockMvc.perform(get(tagsLocation))
.andExpect(jsonPath("_embedded.tags", hasSize(1)))); .andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)))
.andDo(document("get-tags"));
} }
void tagExistingNote(String noteLocation, String tagLocation) throws Exception { void tagExistingNote(String noteLocation, String tagLocation) throws Exception {
Map<String, Object> update = new HashMap<String, Object>(); Map<String, Object> update = new HashMap<String, Object>();
update.put("tags", Arrays.asList(tagLocation)); update.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"tag-existing-note", patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( objectMapper.writeValueAsString(update)))
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
objectMapper.writeValueAsString(update))).andExpect( .andDo(document("tag-existing-note"));
status().isNoContent()));
} }
void getTaggedExistingNote(String tagLocation) throws Exception { void getTaggedExistingNote(String tagLocation) throws Exception {
document("get-tagged-existing-note", this.mockMvc.perform(get(tagLocation)) this.mockMvc.perform(get(tagLocation))
.andExpect(status().isOk())); .andExpect(status().isOk())
.andDo(document("get-tagged-existing-note"));
} }
void getTagsForExistingNote(String taggedNoteLocation) throws Exception { void getTagsForExistingNote(String taggedNoteLocation) throws Exception {
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
.andReturn(), "tags"); .andReturn(), "tags");
document("get-tags-for-existing-note", this.mockMvc.perform(get(tagsLocation))
this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)))); .andExpect(jsonPath("_embedded.tags", hasSize(1)))
.andDo(document("get-tags-for-existing-note"));
} }
private String getLink(MvcResult result, String href) private String getLink(MvcResult result, String href)

View File

@@ -76,32 +76,30 @@ public class ApiDocumentation {
@Test @Test
public void errorExample() throws Exception { public void errorExample() throws Exception {
document( this.mockMvc
"error-example", .perform(get("/error")
this.mockMvc .requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
.perform(get("/error") .requestAttr(RequestDispatcher.ERROR_REQUEST_URI,
.requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400) "/notes")
.requestAttr(RequestDispatcher.ERROR_REQUEST_URI, .requestAttr(RequestDispatcher.ERROR_MESSAGE,
"/notes") "The tag 'http://localhost:8080/tags/123' does not exist"))
.requestAttr(RequestDispatcher.ERROR_MESSAGE, .andDo(print()).andExpect(status().isBadRequest())
"The tag 'http://localhost:8080/tags/123' does not exist"))) .andExpect(jsonPath("error", is("Bad Request")))
.andDo(print()).andExpect(status().isBadRequest()) .andExpect(jsonPath("timestamp", is(notNullValue())))
.andExpect(jsonPath("error", is("Bad Request"))) .andExpect(jsonPath("status", is(400)))
.andExpect(jsonPath("timestamp", is(notNullValue()))) .andExpect(jsonPath("path", is(notNullValue())))
.andExpect(jsonPath("status", is(400))) .andDo(document("error-example"));
.andExpect(jsonPath("path", is(notNullValue())));
} }
@Test @Test
public void indexExample() throws Exception { public void indexExample() throws Exception {
document("index-example", this.mockMvc.perform(get("/"))
this.mockMvc.perform(get("/")) .andExpect(status().isOk())
.andExpect(status().isOk())) .andDo(document("index-example").withLinks(halLinks(),
.andDocumentLinks(halLinks(),
linkWithRel("notes").description( linkWithRel("notes").description(
"The <<resources-notes,Notes resource>>"), "The <<resources-notes,Notes resource>>"),
linkWithRel("tags").description( linkWithRel("tags").description(
"The <<resources-tags,Tags resource>>")); "The <<resources-tags,Tags resource>>")));
} }
@Test @Test
@@ -114,8 +112,9 @@ public class ApiDocumentation {
"http://stateless.co/hal_specification.html"); "http://stateless.co/hal_specification.html");
createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/"); createNote("Application-Level Profile Semantics (ALPS)", "http://alps.io/spec/");
document("notes-list-example", this.mockMvc.perform(get("/notes"))).andExpect( this.mockMvc.perform(get("/notes"))
status().isOk()); .andExpect(status().isOk())
.andDo(document("notes-list-example"));
} }
@Test @Test
@@ -135,12 +134,11 @@ public class ApiDocumentation {
note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html"); note.put("body", "http://martinfowler.com/articles/richardsonMaturityModel.html");
note.put("tags", Arrays.asList(tagLocation)); note.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"notes-create-example", post("/notes").contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(note)))
post("/notes").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
this.objectMapper.writeValueAsString(note))).andExpect( .andDo(document("notes-create-example"));
status().isCreated()));
} }
@Test @Test
@@ -167,16 +165,16 @@ public class ApiDocumentation {
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated()).andReturn().getResponse()
.getHeader("Location"); .getHeader("Location");
document("note-get-example", this.mockMvc.perform(get(noteLocation))) this.mockMvc.perform(get(noteLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("title", is(note.get("title")))) .andExpect(jsonPath("title", is(note.get("title"))))
.andExpect(jsonPath("body", is(note.get("body")))) .andExpect(jsonPath("body", is(note.get("body"))))
.andExpect(jsonPath("_links.self.href", is(noteLocation))) .andExpect(jsonPath("_links.self.href", is(noteLocation)))
.andExpect(jsonPath("_links.note-tags", is(notNullValue()))) .andExpect(jsonPath("_links.note-tags", is(notNullValue())))
.andDocumentLinks(halLinks(), .andDo(document("note-get-example").withLinks(halLinks(),
linkWithRel("self").description("This <<resources-note,note>>"), linkWithRel("self").description("This <<resources-note,note>>"),
linkWithRel("note-tags").description( linkWithRel("note-tags").description(
"This note's <<resources-note-tags,tags>>")); "This note's <<resources-note-tags,tags>>")));
} }
@@ -189,8 +187,9 @@ public class ApiDocumentation {
createTag("Hypermedia"); createTag("Hypermedia");
createTag("HTTP"); createTag("HTTP");
document("tags-list-example", this.mockMvc.perform(get("/tags"))).andExpect( this.mockMvc.perform(get("/tags"))
status().isOk()); .andExpect(status().isOk())
.andDo(document("tags-list-example"));
} }
@Test @Test
@@ -198,12 +197,11 @@ public class ApiDocumentation {
Map<String, String> tag = new HashMap<String, String>(); Map<String, String> tag = new HashMap<String, String>();
tag.put("name", "REST"); tag.put("name", "REST");
document( this.mockMvc.perform(
"tags-create-example", post("/tags").contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(tag)))
post("/tags").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
this.objectMapper.writeValueAsString(tag))).andExpect( .andDo(document("tags-create-example"));
status().isCreated()));
} }
@Test @Test
@@ -238,12 +236,11 @@ public class ApiDocumentation {
Map<String, Object> noteUpdate = new HashMap<String, Object>(); Map<String, Object> noteUpdate = new HashMap<String, Object>();
noteUpdate.put("tags", Arrays.asList(tagLocation)); noteUpdate.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"note-update-example", patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(noteUpdate)))
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
this.objectMapper.writeValueAsString(noteUpdate))) .andDo(document("note-update-example"));
.andExpect(status().isNoContent()));
} }
@Test @Test
@@ -258,15 +255,14 @@ public class ApiDocumentation {
.andExpect(status().isCreated()).andReturn().getResponse() .andExpect(status().isCreated()).andReturn().getResponse()
.getHeader("Location"); .getHeader("Location");
document("tag-get-example", this.mockMvc.perform(get(tagLocation))) this.mockMvc.perform(get(tagLocation))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("name", is(tag.get("name")))) .andExpect(jsonPath("name", is(tag.get("name"))))
.andDocumentLinks(halLinks(), .andDo(document("tag-get-example").withLinks(halLinks(),
linkWithRel("self").description("This <<resources-tag,tag>>"), linkWithRel("self").description("This <<resources-tag,tag>>"),
linkWithRel("tagged-notes") linkWithRel("tagged-notes")
.description( .description(
"The <<resources-tagged-notes,notes>> that have this tag")); "The <<resources-tagged-notes,notes>> that have this tag")));
} }
@Test @Test
@@ -284,12 +280,11 @@ public class ApiDocumentation {
Map<String, Object> tagUpdate = new HashMap<String, Object>(); Map<String, Object> tagUpdate = new HashMap<String, Object>();
tagUpdate.put("name", "RESTful"); tagUpdate.put("name", "RESTful");
document( this.mockMvc.perform(
"tag-update-example", patch(tagLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( this.objectMapper.writeValueAsString(tagUpdate)))
patch(tagLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
this.objectMapper.writeValueAsString(tagUpdate))) .andDo(document("tag-update-example"));
.andExpect(status().isNoContent()));
} }
private void createNote(String title, String body) { private void createNote(String title, String body) {

View File

@@ -71,12 +71,11 @@ public class GettingStartedDocumentation {
@Test @Test
public void index() throws Exception { public void index() throws Exception {
document( this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON))
"index", .andExpect(status().isOk())
this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON)) .andExpect(jsonPath("_links.notes", is(notNullValue())))
.andExpect(status().isOk()) .andExpect(jsonPath("_links.tags", is(notNullValue())))
.andExpect(jsonPath("_links.notes", is(notNullValue()))) .andDo(document("index"));
.andExpect(jsonPath("_links.tags", is(notNullValue()))));
} }
@Test @Test
@@ -101,49 +100,45 @@ public class GettingStartedDocumentation {
note.put("title", "Note creation with cURL"); note.put("title", "Note creation with cURL");
note.put("body", "An example of how to create a note using cURL"); note.put("body", "An example of how to create a note using cURL");
String noteLocation = document( String noteLocation = this.mockMvc
"create-note", .perform(
this.mockMvc post("/notes").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(note)))
post("/notes").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
objectMapper.writeValueAsString(note))) .andExpect(header().string("Location", notNullValue()))
.andExpect(status().isCreated()) .andDo(document("create-note"))
.andExpect(header().string("Location", notNullValue())))
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
return noteLocation; return noteLocation;
} }
void getNote(String noteLocation) throws Exception { void getNote(String noteLocation) throws Exception {
document( this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk())
"get-note", .andExpect(jsonPath("title", is(notNullValue())))
this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk()) .andExpect(jsonPath("body", is(notNullValue())))
.andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("_links.note-tags", is(notNullValue())))
.andExpect(jsonPath("body", is(notNullValue()))) .andDo(document("get-note"));
.andExpect(jsonPath("_links.note-tags", is(notNullValue()))));
} }
String createTag() throws Exception, JsonProcessingException { String createTag() throws Exception, JsonProcessingException {
Map<String, String> tag = new HashMap<String, String>(); Map<String, String> tag = new HashMap<String, String>();
tag.put("name", "getting-started"); tag.put("name", "getting-started");
String tagLocation = document( String tagLocation = this.mockMvc
"create-tag", .perform(
this.mockMvc post("/tags").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(tag)))
post("/tags").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
objectMapper.writeValueAsString(tag))) .andExpect(header().string("Location", notNullValue()))
.andExpect(status().isCreated()) .andDo(document("create-tag"))
.andExpect(header().string("Location", notNullValue())))
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
return tagLocation; return tagLocation;
} }
void getTag(String tagLocation) throws Exception { void getTag(String tagLocation) throws Exception {
document( this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk())
"get-tag", .andExpect(jsonPath("name", is(notNullValue())))
this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("_links.tagged-notes", is(notNullValue())))
.andExpect(jsonPath("name", is(notNullValue()))) .andDo(document("get-tag"));
.andExpect(jsonPath("_links.tagged-notes", is(notNullValue()))));
} }
String createTaggedNote(String tag) throws Exception { String createTaggedNote(String tag) throws Exception {
@@ -152,59 +147,60 @@ public class GettingStartedDocumentation {
note.put("body", "An example of how to create a tagged note using cURL"); note.put("body", "An example of how to create a tagged note using cURL");
note.put("tags", Arrays.asList(tag)); note.put("tags", Arrays.asList(tag));
String noteLocation = document( String noteLocation = this.mockMvc
"create-tagged-note", .perform(
this.mockMvc post("/notes").contentType(MediaTypes.HAL_JSON).content(
.perform( objectMapper.writeValueAsString(note)))
post("/notes").contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isCreated())
objectMapper.writeValueAsString(note))) .andExpect(header().string("Location", notNullValue()))
.andExpect(status().isCreated()) .andDo(document("create-tagged-note"))
.andExpect(header().string("Location", notNullValue())))
.andReturn().getResponse().getHeader("Location"); .andReturn().getResponse().getHeader("Location");
return noteLocation; return noteLocation;
} }
void getTaggedNote(String tagLocation) throws Exception { void getTaggedNote(String tagLocation) throws Exception {
document( this.mockMvc.perform(get(tagLocation))
"get-tagged-note", .andExpect(status().isOk())
this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk()) .andExpect(jsonPath("title", is(notNullValue())))
.andExpect(jsonPath("title", is(notNullValue()))) .andExpect(jsonPath("body", is(notNullValue())))
.andExpect(jsonPath("body", is(notNullValue()))) .andExpect(jsonPath("_links.note-tags", is(notNullValue())))
.andExpect(jsonPath("_links.note-tags", is(notNullValue())))); .andDo(document("get-tagged-note"));
} }
void getTags(String taggedNoteLocation) throws Exception { void getTags(String taggedNoteLocation) throws Exception {
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
.andReturn(), "note-tags"); .andReturn(), "note-tags");
document("get-tags", this.mockMvc.perform(get(tagsLocation))
this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)))); .andExpect(jsonPath("_embedded.tags", hasSize(1)))
.andDo(document("get-tags"));
} }
void tagExistingNote(String noteLocation, String tagLocation) throws Exception { void tagExistingNote(String noteLocation, String tagLocation) throws Exception {
Map<String, Object> update = new HashMap<String, Object>(); Map<String, Object> update = new HashMap<String, Object>();
update.put("tags", Arrays.asList(tagLocation)); update.put("tags", Arrays.asList(tagLocation));
document( this.mockMvc.perform(
"tag-existing-note", patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
this.mockMvc.perform( objectMapper.writeValueAsString(update)))
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content( .andExpect(status().isNoContent())
objectMapper.writeValueAsString(update))).andExpect( .andDo(document("tag-existing-note"));
status().isNoContent()));
} }
void getTaggedExistingNote(String tagLocation) throws Exception { void getTaggedExistingNote(String tagLocation) throws Exception {
document("get-tagged-existing-note", this.mockMvc.perform(get(tagLocation)) this.mockMvc.perform(get(tagLocation))
.andExpect(status().isOk())); .andExpect(status().isOk())
.andDo(document("get-tagged-existing-note"));
} }
void getTagsForExistingNote(String taggedNoteLocation) throws Exception { void getTagsForExistingNote(String taggedNoteLocation) throws Exception {
String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation)) String tagsLocation = getLink(this.mockMvc.perform(get(taggedNoteLocation))
.andReturn(), "note-tags"); .andReturn(), "note-tags");
document("get-tags-for-existing-note", this.mockMvc.perform(get(tagsLocation))
this.mockMvc.perform(get(tagsLocation)).andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(jsonPath("_embedded.tags", hasSize(1)))); .andExpect(jsonPath("_embedded.tags", hasSize(1)))
.andDo(document("get-tags-for-existing-note"));
} }
private String getLink(MvcResult result, String rel) private String getLink(MvcResult result, String rel)

View File

@@ -16,36 +16,26 @@
package org.springframework.restdocs.core; package org.springframework.restdocs.core;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse;
import java.util.Map; import java.util.Map;
import org.springframework.test.web.servlet.ResultActions;
public class RestDocumentation { public class RestDocumentation {
public static RestDocumentationResultActions document(String outputDir, public static RestDocumentationResultHandler document(String outputDir) throws Exception {
ResultActions resultActions) throws Exception { return new RestDocumentationResultHandler(outputDir);
return new RestDocumentationResultActions(outputDir, resultActions) }
.andDo(documentCurlRequest(outputDir).includeResponseHeaders())
.andDo(documentCurlResponse(outputDir).includeResponseHeaders())
.andDo(documentCurlRequestAndResponse(outputDir).includeResponseHeaders());
}
public static LinkDescriptor linkWithRel(String rel) { public static LinkDescriptor linkWithRel(String rel) {
return new LinkDescriptor(rel); return new LinkDescriptor(rel);
} }
public static LinkExtractor halLinks() { public static LinkExtractor halLinks() {
return new LinkExtractor() { return new LinkExtractor() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Map<String, Object> extractLinks(Map<String, Object> responseJson) { public Map<String, Object> extractLinks(Map<String, Object> responseJson) {
return (Map<String, Object>) responseJson.get("_links"); return (Map<String, Object>) responseJson.get("_links");
} }
}; };
} }
} }

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.core;
import java.util.Arrays;
import org.springframework.restdocs.core.RestDocumentationResultHandlers.LinkDocumentingResultHandler;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultHandler;
import org.springframework.test.web.servlet.ResultMatcher;
public class RestDocumentationResultActions implements ResultActions {
private final ResultActions delegate;
private final String outputDir;
public RestDocumentationResultActions(String outputDir, ResultActions delegate) {
this.outputDir = outputDir;
this.delegate = delegate;
}
@Override
public RestDocumentationResultActions andExpect(ResultMatcher matcher)
throws Exception {
this.delegate.andExpect(matcher);
return this;
}
@Override
public RestDocumentationResultActions andDo(ResultHandler handler) throws Exception {
this.delegate.andDo(handler);
return this;
}
@Override
public MvcResult andReturn() {
return this.delegate.andReturn();
}
public RestDocumentationResultActions andDocumentLinks(LinkExtractor linkExtractor, LinkDescriptor... descriptors)
throws Exception {
this.delegate.andDo(new LinkDocumentingResultHandler(this.outputDir, linkExtractor, Arrays
.asList(descriptors)));
return this;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.restdocs.core;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse;
import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse;
import java.util.Arrays;
import org.springframework.restdocs.core.RestDocumentationResultHandlers.LinkDocumentingResultHandler;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultHandler;
public class RestDocumentationResultHandler implements ResultHandler {
private final String outputDir;
private ResultHandler linkDocumentingResultHandler;
public RestDocumentationResultHandler(String outputDir) {
this.outputDir = outputDir;
}
@Override
public void handle(MvcResult result) throws Exception {
documentCurlRequest(outputDir).includeResponseHeaders().handle(result);
documentCurlResponse(outputDir).includeResponseHeaders().handle(result);
documentCurlRequestAndResponse(outputDir).includeResponseHeaders().handle(result);
if(linkDocumentingResultHandler != null) {
linkDocumentingResultHandler.handle(result);
}
}
public RestDocumentationResultHandler withLinks(LinkExtractor linkExtractor, LinkDescriptor... descriptors) {
linkDocumentingResultHandler = new LinkDocumentingResultHandler(outputDir, linkExtractor, Arrays.asList(descriptors));
return this;
}
}