Spring REST 文档测试中未使用自定义 Jackson 模块
Custom Jackson module not used in Spring REST docs test
我用自定义 Jackson 模块(使用 Spring Boot 1.3)在 Spring REST 文档上写了一个小测试。在我的主应用程序 class 中,我只有 @SpringBootApplication
。然后我有另一个 class 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 启动应用程序时,我看到 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。
NoteControllerDocumentation
未配置为使用 Spring 引导创建应用程序上下文。这意味着 Spring 引导的自动配置不会 运行,因此,您的自定义 Jackson 模块不会应用于 ObjectMapper
。
解决您问题的最简单方法是删除 TestConfiguration
class 并更新 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."))));
}
}
或者,如果您想更好地控制控件的创建方式(例如,注入模拟服务),您可以使用自定义配置 class。关键是将 class 注释为 @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();
}
}
}
我用自定义 Jackson 模块(使用 Spring Boot 1.3)在 Spring REST 文档上写了一个小测试。在我的主应用程序 class 中,我只有 @SpringBootApplication
。然后我有另一个 class 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 启动应用程序时,我看到 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。
NoteControllerDocumentation
未配置为使用 Spring 引导创建应用程序上下文。这意味着 Spring 引导的自动配置不会 运行,因此,您的自定义 Jackson 模块不会应用于 ObjectMapper
。
解决您问题的最简单方法是删除 TestConfiguration
class 并更新 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."))));
}
}
或者,如果您想更好地控制控件的创建方式(例如,注入模拟服务),您可以使用自定义配置 class。关键是将 class 注释为 @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();
}
}
}