[spring] REST Docs 사용중 urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?

@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@SpringBootTest
public class BookControllerTest {
    private MockMvc mockMvc;
    @Rule
    public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

    @Autowired
    WebApplicationContext wac;
    @MockBean
    BookService bookService;

    @Before
    public void setUp() {
        //mockBookController 내에 Mock 처리된 bookService를 주입하기 위해 반드시 선언해줘야 함
        MockitoAnnotations.initMocks(this);

        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                .apply(documentationConfiguration(this.restDocumentation))
                .alwaysDo(print())
                .build();
    }

    @Test
    public void testOptions() throws Exception {
        this.mockMvc.perform(options("/books").contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andDo(document("books-options"));
    }

    @Test
    public void testHead() throws Exception {
        Long id = 1L;
        when(bookService.findById(id)).thenReturn(Optional.of(new Book("test-book", "test-isbn13", "test-isbn10")));

        this.mockMvc.perform(head("/books/{id}", 1).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(MockMvcResultMatchers.status().isNoContent())
                .andDo(document("books-head"));
    }

    @Test
    public void testGet() throws Exception {
        Long id = 1L;
        when(bookService.findById(1L)).thenReturn(Optional.of(new Book("test-book", "test-isbn13", "test-isbn10")));

        this.mockMvc.perform(get("/books/{id}", id).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.name", Is.is("test-book")))
                .andExpect(jsonPath("$.data.isbn13", Is.is("test-isbn13")))
                .andExpect(jsonPath("$.data.isbn10", Is.is("test-isbn10")))
                .andDo(document("books-get", pathParameters(
                        parameterWithName("id").description("도서 참조키")
                )));
    }
}

다음과 같이 코드를 짰다. testGet 에서 document 선언을 하는 부분에서 pathParameters 를 추가했더니 다음과 같은 오류 메시지가 출력된다.

java.lang.IllegalArgumentException: urlTemplate not found. If you are using MockMvc did you use RestDocumentationRequestBuilders to build the request?

	at org.springframework.util.Assert.notNull(Assert.java:193)
	at org.springframework.restdocs.request.PathParametersSnippet.extractUrlTemplate(PathParametersSnippet.java:132)
	at org.springframework.restdocs.request.PathParametersSnippet.extractActualParameters(PathParametersSnippet.java:119)
	at org.springframework.restdocs.request.AbstractParametersSnippet.verifyParameterDescriptors(AbstractParametersSnippet.java:95)
	at org.springframework.restdocs.request.AbstractParametersSnippet.createModel(AbstractParametersSnippet.java:79)
	at org.springframework.restdocs.request.PathParametersSnippet.createModel(PathParametersSnippet.java:104)
	at org.springframework.restdocs.snippet.TemplatedSnippet.document(TemplatedSnippet.java:83)
	at org.springframework.restdocs.generate.RestDocumentationGenerator.handle(RestDocumentationGenerator.java:206)
	at org.springframework.restdocs.mockmvc.RestDocumentationResultHandler.handle(RestDocumentationResultHandler.java:55)
	at org.springframework.test.web.servlet.MockMvc$1.andDo(MockMvc.java:183)
	at io.honeymon.springboot.t.bookstore.api.controller.BookControllerTest.testGet(BookControllerTest.java:90)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)
	at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.restdocs.JUnitRestDocumentation$1.evaluate(JUnitRestDocumentation.java:63)
	at org.junit.rules.RunRules.evaluate(RunRules.java:20)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

찾아보니 pathParameters를 사용할거면 MockMvcBuilders 보다 RestDocumentationRequestBuilders를 이용하는 것이 좋다고 한다.

To make the path parameters available for documentation, the request must be built using one of the methods on RestDocumentationRequestBuilders rather than MockMvcRequestBuilders.

코드를 다음과 같이 변경하면 된다.

@Test
public void testGet() throws Exception {
    Long id = 1L;
    when(bookService.findById(1L)).thenReturn(Optional.of(new Book("test-book", "test-isbn13", "test-isbn10")));


    this.mockMvc.perform(MockMvcRequestBuilders.get("/books/{id}", // <1> id).contentType(MediaType.APPLICATION_JSON_UTF8))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.data.name", Is.is("test-book")))
            .andExpect(jsonPath("$.data.isbn13", Is.is("test-isbn13")))
            .andExpect(jsonPath("$.data.isbn10", Is.is("test-isbn10")))
            .andDo(document("books-get", pathParameters(
                    parameterWithName("id").description("도서 참조키")
            )));
}

@Test
    public void testGet() throws Exception {
        Long id = 1L;
        when(bookService.findById(1L)).thenReturn(Optional.of(new Book("test-book", "test-isbn13", "test-isbn10")));


        this.mockMvc.perform(RestDocumentationRequestBuilders.get("/books/{id}", // <2> id).contentType(MediaType.APPLICATION_JSON_UTF8))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.data.name", Is.is("test-book")))
                .andExpect(jsonPath("$.data.isbn13", Is.is("test-isbn13")))
                .andExpect(jsonPath("$.data.isbn10", Is.is("test-isbn10")))
                .andDo(document("books-get", pathParameters(
                        parameterWithName("id").description("도서 참조키")
                )));
    }
  1. MockMvcRequestBuilders 을 <2> MockMvcRequestBuilders 으로 변경하면 된다.

프로그래밍 작업에 있어서 예제를 보면서 작업을 하는 것이 매우 효율적이라는 연구들이 많은데 정작 API에 대해서 가장 먼저 배울수 있는 자바 Doc등의 API 문서에는 매우 적은 양의 API가 포함되어 있다는 것이다.(저희가 조사한바에 Java6 API 문서는 1% 의 메소드에만 예제가 달려 있다.)
출처 -  HannKim, 소프트웨어 스토리(http://www.se.or.kr/16) 중

  자바를 공부하면서, 새로운 클래스와 메소드를 찾아서 사용해야할 때 많이 사용하는 API 문서.

  ^^ 자바의 바이블이라고도 할 수 있는 API 문서 내에서 자동으로 API문서 관련한 예제를 찾아준다면, 나와 같은 초보를 비롯해서 API를 수시로 열여봐야 하는 프로그래머라면 누구든지 대환영을 할 것으로 여겨진다. ^^

API문서 생성기 내에 예제 검색 로봇이 탑재되는 방식일까요? ^^
API문서를 작성하는 사람이 선택하는 방식!? 과연 어떤 방식으로 나올까요!? ^^

저의 짧은 의견을 제시하자면, API 문서의 예제를 별도로 관리하는 API 예제 서버를 만들어서, 거기에서 새로운 업데이트가 있을 때마다 업데이트가 진행되는 방식은 어떨까요? ^^; 이 예제는 Wiki - 백과 처럼 작성을 하는 방법도 좋지 않을까 생각합니다.

● 예제를 테스트해볼 수 있는 곳 :  http://ids.postech.ac.kr:8080/exoaExamples/api/
● 한나님이 OKJSP에 올리신 글 : http://www.okjsp.pe.kr/seq/137424

사진 출처 : http://farm4.static.flickr.com/3298/3507015519_7a22bb5cd3.jpg



꼬오오옥!! >ㅅ< 완성해주십시오. ㅎㅎ.

+ Recent posts