【问题标题】:Custom Jackson module not used in Spring REST docs testSpring REST 文档测试中未使用的自定义 Jackson 模块
【发布时间】:2016-03-13 10:26:01
【问题描述】:

我已经使用自定义 Jackson 模块(使用 Spring Boot 1.3)在 Spring REST 文档上编写了一个小测试。在我的应用程序主类中,我只有@SpringBootApplication。然后我有另一个类JacksonCustomizations,看起来像这样:

@Configuration
public class JacksonCustomizations {

@Bean
public Module myCustomModule() {
    return new MyCustomModule();
}

static class MyCustomModule extends SimpleModule {
    public MyCustomModule() {

        addSerializer(ImmutableEntityId.class, new JsonSerializer<ImmutableEntityId>() {
            @Override
            public void serialize(ImmutableEntityId immutableEntityId, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
                jsonGenerator.writeNumber( (Long)immutableEntityId.getId() );
            }
        });
    }
}
}

这种定制是完美的。当我运行 Spring Boot 应用程序时,我看到了应有的 JSON。

但是,在我的文档测试中,没有应用自定义。这是我的测试代码:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
@WebAppConfiguration
public class NoteControllerDocumentation {

@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");

@Autowired
private WebApplicationContext context;

private MockMvc mockMvc;

@Before
public void setUp() throws Exception {
    mockMvc = MockMvcBuilders.webAppContextSetup(context)
                             .apply(documentationConfiguration(restDocumentation))
                             .build();

}


@Test
public void notesListExample() throws Exception {
    mockMvc.perform(get("/api/notes/"))
           .andExpect(status().isOk())
           .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
           .andDo(document("notes-list-example", responseFields(
                   fieldWithPath("[]").description("An array of <<note-example,note>>s."))));
}

@Configuration
@EnableWebMvc
@Import(JacksonCustomizations.class)
public static class TestConfiguration {
    @Bean
    public NoteController noteController() {
        return new NoteController();
    }
}

}

注意我的测试中的应用程序上下文如何导入JacksonCustomizations 配置。

我发现的其他东西:

  • 在我的启动应用程序中添加@EnableWebMvc 会使自定义停止工作。
  • 删除我的测试中的 @EnableWebMvc 会停止生成 JSON。

【问题讨论】:

  • 您是否尝试将 @Import(JacksonCustomizations.class) 添加到 NoteControllerDocumentation 类?
  • @reos 没有区别
  • 听起来 Spring Boot 没有使用您的自定义 Module 自动配置 ObjectMapper。这与您没有为您的应用程序提供类(es)的SpringApplicationConfiguration 的事实相匹配,因此 Spring Boot 不知道该怎么做。事实上,您共享的代码失败并显示 IllegalStateException,因为这个原因,“在 @SpringApplicationConfiguration 中找不到配置类或位置”。您能否分享一个包含您正在运行以触发问题的确切代码的小型示例项目?
  • ContextConfiguration 不是自动使用内部类Configuration 吗?请从dl.dropboxusercontent.com/u/6373261/so-34156932.zip下载显示问题的示例项目。
  • 啊,不清楚(格式有点不对)TestConfiguration 是一个内部类。感谢您的示例项目。我已经在下面发布了答案。

标签: java spring spring-mvc spring-boot spring-restdocs


【解决方案1】:

NoteControllerDocumentation 未配置为使用 Spring Boot 创建应用程序上下文。这意味着 Spring Boot 的自动配置不会运行,因此,您的自定义 Jackson 模块不会应用于 ObjectMapper

解决您的问题的最简单方法是删除您的TestConfiguration 类并更新SpringApplicationConfiguration 以引用DemoApplication。这将为您留下以下代码:

package com.example.controller;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.example.DemoApplication;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = DemoApplication.class)
@WebAppConfiguration
public class NoteControllerDocumentation {

    @Rule
    public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                                 .apply(documentationConfiguration(restDocumentation))
                                 .build();

    }

    @Test
    public void notesListExample() throws Exception {
        mockMvc.perform(get("/api/notes/"))
               .andExpect(status().isOk())
               .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
               .andExpect(content().json("[{\"id\":1}]"))
               .andDo(print())
               .andDo(document("nodes-list-example", responseFields(
                       fieldWithPath("[]").description("An array of <<note-example,note>>s."))));
    }

}

或者,如果您希望对控件的创建方式进行更多控制(例如,注入模拟服务),您可以使用自定义配置类。关键是用 @EnableAutoConfiguration 注释该类,以便启用 Spring Boot 的自动配置并执行 ObjectMapper 的自定义。这种方法会给你以下代码:

package com.example.controller;

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.MediaType;
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.example.JacksonCustomizations;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration
@WebAppConfiguration
public class NoteControllerDocumentation {

    @Rule
    public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;

    @Before
    public void setUp() throws Exception {
        mockMvc = MockMvcBuilders.webAppContextSetup(context)
                                 .apply(documentationConfiguration(restDocumentation))
                                 .build();

    }

    @Test
    public void notesListExample() throws Exception {
        mockMvc.perform(get("/api/notes/"))
               .andExpect(status().isOk())
               .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
               .andExpect(content().json("[{\"id\":1}]"))
               .andDo(print())
               .andDo(document("nodes-list-example", responseFields(
                       fieldWithPath("[]").description("An array of <<note-example,note>>s."))));
    }

    @Configuration
    @EnableAutoConfiguration
    @Import(JacksonCustomizations.class)
    static class TestConfiguration {

        @Bean
        public NoteController notesController() {
            return new NoteController();
        }

    }

}

【讨论】:

  • 但是如果我的控制器使用自动连接的服务或存储库怎么办?我怎样才能为它设置一个模拟?引用 DemoApplication 将注入真正的服务/存储库。
  • 你没有在问题中提到这个要求,所以我没有解决它。我已经用另一种方法的详细信息更新了我的答案。
猜你喜欢
  • 2018-10-20
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 2014-02-19
  • 1970-01-01
  • 2017-08-19
相关资源
最近更新 更多