증상

Java 8을 기본으로 개발을 해오고 있다. 그러다가 외부 교육을 할 기회가 있었는데, 이 때 교육생들은 대부분 윈도우즈를 사용하고 있었고, 나와는 다르게 오라클 Java Download 환경에서 11 혹은 10 버전을 설치운영하고 있었다.

Java 10 실행환경에서 스프링 부트 프로젝트를 실행하면 Caused by: java.lang.ClassNotFoundException: javax.xml.bind.JAXBException 가 발생한다.

JAVA 9부터 빠진 기능들이 있다.

http://openjdk.java.net/jeps/320

  • java.xml.ws (JAX-WS, plus the related technologies SAAJ and Web Services Metadata)
  • java.xml.bind (JAXB)
  • java.activation (JAF)
  • java.xml.ws.annotation (Common Annotations)
  • java.corba (CORBA)
  • java.transaction (JTA)
대충 살펴보면 XML 과 관련된 모듈이 분리됐다.



해결방법

프로젝트 의존성 내에 compile "javax.xml.bind:jaxb-api" 을 추가한다.
스프링 부트에서는 의존성 관리기능을 통해 jaxb-api 버전을 관리하고 있기 때문에 굳이 버전을 신경쓰지 않아도 된다.



지난 2018/09/20 티아카데미에서 '스프링 부트로 웹 서비스 개발하기' 라는 짧은 강연에서 사용한 발표자료 3/3 번째 발표자료


지난 2018/09/20 티아카데미에서 '스프링 부트로 웹 서비스 개발하기' 라는 짧은 강연에서 사용한 발표자료 2/3 번째 발표자료


지난 2018/09/20 티아카데미에서 '스프링 부트로 웹 서비스 개발하기' 라는 짧은 강연에서 사용한 발표자료 1/3 번째 발표자료


[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 으로 변경하면 된다.

+ Recent posts