自定义的 ObjectMapper 未在测试中使用
Customized ObjectMapper not used in test
我正在使用 Spring 框架,版本 4.1.6,带有 Spring 网络服务,没有 Spring 启动。为了学习该框架,我正在编写一个 REST API 并进行测试以确保从命中端点接收到的 JSON 响应是正确的。具体来说,我正在尝试调整 ObjectMapper
的 PropertyNamingStrategy
以使用 "lower case with underscores" 命名策略。
我正在使用 the method detailed on Spring's blog 创建一个新的 ObjectMapper
并将其添加到转换器列表中。如下:
package com.myproject.config;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = jacksonBuilder();
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
}
然后我运行进行以下测试(使用JUnit、MockMvc和Mockito)来验证我的更改:
package com.myproject.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// Along with other application imports...
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebConfig.class}, loader = AnnotationConfigWebContextLoader.class)
public class MyControllerTest {
@Mock
private MyManager myManager;
@InjectMocks
private MyController myController;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(this.myController).build();
}
@Test
public void testMyControllerWithNameParam() throws Exception {
MyEntity expected = new MyEntity();
String name = "expected";
String title = "expected title";
// Set up MyEntity with data.
expected.setId(1); // Random ID.
expected.setEntityName(name);
expected.setEntityTitle(title)
// When the MyManager instance is asked for the MyEntity with name parameter,
// return expected.
when(this.myManager.read(name)).thenReturn(expected);
// Assert the proper results.
MvcResult result = mockMvc.perform(
get("/v1/endpoint")
.param("name", name))
.andExpect(status().isOk())
.andExpect((content().contentType("application/json;charset=UTF-8")))
.andExpect(jsonPath("$.entity_name", is(name))))
.andExpect(jsonPath("$.entity_title", is(title)))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
但是,这个 returns 的响应是:
{"id": 1, "entityName": "expected", "entityTitle": "expected title"}
我什么时候应该得到:
{"id": 1, "entity_name": "expected", "entity_title": "expected title"}
我有一个扫描包的已实现的 WebApplicationInitializer:
package com.myproject.config;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.scan("com.myproject.config");
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
servletContext.addListener(new ContextLoaderListener(ctx));
}
}
在 IntelliJ 中使用我的调试器,我可以看到生成器已创建并添加,但在某处生成的 ObjectMapper
并未实际使用。我一定是遗漏了什么,但我设法找到的所有例子似乎都没有提到那是什么!我已经尝试消除 @EnableWebMvc
并实现 WebMvcConfigurationSupport
,使用 MappingJackson2HttpMessageConverter
作为 Bean,并将 ObjectMapper
设置为 Bean 无济于事。
如有任何帮助,我们将不胜感激!如果需要任何其他文件,请告诉我。
谢谢!
编辑: 进行了更多挖掘,发现 this。在 link 中,作者在 he/she 构建 MockMvc 之前附加了 setMessageConverters()
并且它适用于作者。做同样的事情对我也有用;但是,我不确定是否所有内容都可以在生产中使用,因为存储库尚未清除。当我发现我会提交一个答案。
编辑 2: 查看答案。
我了解了为什么会这样工作。重申一下,让我的自定义 ObjectMapper 在我的测试中工作的过程(假设 MockMvc 被创建为独立的)如下:
- 创建一个扩展
WebMvcConfigurerAdapter
. 的 WebConfig
class
- 在
WebConfig
class中新建一个@Bean
,即returns一个MappingJackson2HttpMessageConverter
。这个 MappingJackson2HttpMessageConverter
应用了所需的更改(在我的例子中,它传递给它一个 Jackson2ObjectMapperBuilder
,PropertyNamingStrategy
设置为 CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
。)
- 同样在
WebConfig
class、@Override
configureMessageConverters()
中并将 (2) 中的 MappingJackson2HttpMessageConverter
添加到消息转换器列表中。
- 在测试文件中,添加一个
@ContextConfiguration(classes = { WebConfig.class })
注解来告知测试你的@Bean
。
- 使用
@Autowired
注入并访问(2)中定义的@Bean
。
- 在
MockMvc
的设置中,使用 .setMessageConverters()
方法并将其传递给注入的 MappingJackson2HttpMessageConverter
。测试现在将使用 (2) 中设置的配置。
测试文件:
package com.myproject.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// Along with other application imports...
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebConfig.class})
public class MyControllerTest {
/**
* Note that the converter needs to be autowired into the test in order for
* MockMvc to recognize it in the setup() method.
*/
@Autowired
private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;
@Mock
private MyManager myManager;
@InjectMocks
private MyController myController;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders
.standaloneSetup(this.myController)
.setMessageConverters(this.jackson2HttpMessageConverter) // Important!
.build();
}
@Test
public void testMyControllerWithNameParam() throws Exception {
MyEntity expected = new MyEntity();
String name = "expected";
String title = "expected title";
// Set up MyEntity with data.
expected.setId(1); // Random ID.
expected.setEntityName(name);
expected.setEntityTitle(title)
// When the MyManager instance is asked for the MyEntity with name parameter,
// return expected.
when(this.myManager.read(name)).thenReturn(expected);
// Assert the proper results.
MvcResult result = mockMvc.perform(
get("/v1/endpoint")
.param("name", name))
.andExpect(status().isOk())
.andExpect((content().contentType("application/json;charset=UTF-8")))
.andExpect(jsonPath("$.entity_name", is(name))))
.andExpect(jsonPath("$.entity_title", is(title)))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
和配置文件:
package com.myproject.config;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jackson2HttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
Jackson2ObjectMapperBuilder builder = this.jacksonBuilder();
converter.setObjectMapper(builder.build());
return converter;
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
}
将我生成的 WAR 文件部署到 Tomcat 7 in XAMPP 表明命名策略使用正确。我相信它之所以如此工作,是因为在独立设置中,除非另有说明,否则始终使用一组默认的消息转换器。这可以在 StandAloneMockMvcBuilder.java(版本 4.1.6,\org\springframework\test\web\servlet\setup\StandaloneMockMvcBuilder.java
)中的 setMessageConverters()
函数的注释中看到:
/**
* Set the message converters to use in argument resolvers and in return value
* handlers, which support reading and/or writing to the body of the request
* and response. If no message converters are added to the list, a default
* list of converters is added instead.
*/
public StandaloneMockMvcBuilder setMessageConverters(HttpMessageConverter<?>...messageConverters) {
this.messageConverters = Arrays.asList(messageConverters);
return this;
}
因此,如果 MockMvc 在构建 MockMvc 期间未明确告知对消息转换器的更改,它将不会使用这些更改。
或者你可以
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new
MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper( new ObjectMapper().setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) );
mockMvc = MockMvcBuilders.standaloneSetup(attributionController).setMessageConverters(
mappingJackson2HttpMessageConverter ).build();
使用 Spring Boot 1.5.1 我可以做到:
@RunWith(SpringRunner.class)
@AutoConfigureJsonTesters
@JsonTest
public class JsonTest {
@Autowired
ObjectMapper objectMapper;
}
以与运行时相同的方式访问配置的 ObjectMapper。
我的runtime jackson是这样配置的:
@Configuration
public class JacksonConfiguration {
@Autowired
Environment environment;
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
builder.locale(new Locale("sv", "SE"));
if (JacksonConfiguration.this.environment == null
|| !JacksonConfiguration.this.environment.acceptsProfiles("docker")) {
builder.indentOutput(true);
}
final Jdk8Module jdk8Module = new Jdk8Module();
final ProblemModule problemModule = new ProblemModule();
final JavaTimeModule javaTimeModule = new JavaTimeModule();
final Module[] modules = new Module[] { jdk8Module, problemModule,
javaTimeModule };
builder.modulesToInstall(modules);
};
}
}
在 Spring 引导中,当对控制器层 (@WebMvcTest) 进行单元测试时,您可以访问对象映射器,因此您可以在测试用例之前对其进行修改:
@Autowired
private ObjectMapper objectMapper;
@Before
public void init(){
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}
我正在使用 Spring 框架,版本 4.1.6,带有 Spring 网络服务,没有 Spring 启动。为了学习该框架,我正在编写一个 REST API 并进行测试以确保从命中端点接收到的 JSON 响应是正确的。具体来说,我正在尝试调整 ObjectMapper
的 PropertyNamingStrategy
以使用 "lower case with underscores" 命名策略。
我正在使用 the method detailed on Spring's blog 创建一个新的 ObjectMapper
并将其添加到转换器列表中。如下:
package com.myproject.config;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = jacksonBuilder();
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
}
然后我运行进行以下测试(使用JUnit、MockMvc和Mockito)来验证我的更改:
package com.myproject.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// Along with other application imports...
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebConfig.class}, loader = AnnotationConfigWebContextLoader.class)
public class MyControllerTest {
@Mock
private MyManager myManager;
@InjectMocks
private MyController myController;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(this.myController).build();
}
@Test
public void testMyControllerWithNameParam() throws Exception {
MyEntity expected = new MyEntity();
String name = "expected";
String title = "expected title";
// Set up MyEntity with data.
expected.setId(1); // Random ID.
expected.setEntityName(name);
expected.setEntityTitle(title)
// When the MyManager instance is asked for the MyEntity with name parameter,
// return expected.
when(this.myManager.read(name)).thenReturn(expected);
// Assert the proper results.
MvcResult result = mockMvc.perform(
get("/v1/endpoint")
.param("name", name))
.andExpect(status().isOk())
.andExpect((content().contentType("application/json;charset=UTF-8")))
.andExpect(jsonPath("$.entity_name", is(name))))
.andExpect(jsonPath("$.entity_title", is(title)))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
但是,这个 returns 的响应是:
{"id": 1, "entityName": "expected", "entityTitle": "expected title"}
我什么时候应该得到:
{"id": 1, "entity_name": "expected", "entity_title": "expected title"}
我有一个扫描包的已实现的 WebApplicationInitializer:
package com.myproject.config;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class WebAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.scan("com.myproject.config");
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
servletContext.addListener(new ContextLoaderListener(ctx));
}
}
在 IntelliJ 中使用我的调试器,我可以看到生成器已创建并添加,但在某处生成的 ObjectMapper
并未实际使用。我一定是遗漏了什么,但我设法找到的所有例子似乎都没有提到那是什么!我已经尝试消除 @EnableWebMvc
并实现 WebMvcConfigurationSupport
,使用 MappingJackson2HttpMessageConverter
作为 Bean,并将 ObjectMapper
设置为 Bean 无济于事。
如有任何帮助,我们将不胜感激!如果需要任何其他文件,请告诉我。
谢谢!
编辑: 进行了更多挖掘,发现 this。在 link 中,作者在 he/she 构建 MockMvc 之前附加了 setMessageConverters()
并且它适用于作者。做同样的事情对我也有用;但是,我不确定是否所有内容都可以在生产中使用,因为存储库尚未清除。当我发现我会提交一个答案。
编辑 2: 查看答案。
我了解了为什么会这样工作。重申一下,让我的自定义 ObjectMapper 在我的测试中工作的过程(假设 MockMvc 被创建为独立的)如下:
- 创建一个扩展
WebMvcConfigurerAdapter
. 的 - 在
WebConfig
class中新建一个@Bean
,即returns一个MappingJackson2HttpMessageConverter
。这个MappingJackson2HttpMessageConverter
应用了所需的更改(在我的例子中,它传递给它一个Jackson2ObjectMapperBuilder
,PropertyNamingStrategy
设置为CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
。) - 同样在
WebConfig
class、@Override
configureMessageConverters()
中并将 (2) 中的MappingJackson2HttpMessageConverter
添加到消息转换器列表中。 - 在测试文件中,添加一个
@ContextConfiguration(classes = { WebConfig.class })
注解来告知测试你的@Bean
。 - 使用
@Autowired
注入并访问(2)中定义的@Bean
。 - 在
MockMvc
的设置中,使用.setMessageConverters()
方法并将其传递给注入的MappingJackson2HttpMessageConverter
。测试现在将使用 (2) 中设置的配置。
WebConfig
class
测试文件:
package com.myproject.controller;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.AnnotationConfigWebContextLoader;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
// Along with other application imports...
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = {WebConfig.class})
public class MyControllerTest {
/**
* Note that the converter needs to be autowired into the test in order for
* MockMvc to recognize it in the setup() method.
*/
@Autowired
private MappingJackson2HttpMessageConverter jackson2HttpMessageConverter;
@Mock
private MyManager myManager;
@InjectMocks
private MyController myController;
private MockMvc mockMvc;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders
.standaloneSetup(this.myController)
.setMessageConverters(this.jackson2HttpMessageConverter) // Important!
.build();
}
@Test
public void testMyControllerWithNameParam() throws Exception {
MyEntity expected = new MyEntity();
String name = "expected";
String title = "expected title";
// Set up MyEntity with data.
expected.setId(1); // Random ID.
expected.setEntityName(name);
expected.setEntityTitle(title)
// When the MyManager instance is asked for the MyEntity with name parameter,
// return expected.
when(this.myManager.read(name)).thenReturn(expected);
// Assert the proper results.
MvcResult result = mockMvc.perform(
get("/v1/endpoint")
.param("name", name))
.andExpect(status().isOk())
.andExpect((content().contentType("application/json;charset=UTF-8")))
.andExpect(jsonPath("$.entity_name", is(name))))
.andExpect(jsonPath("$.entity_title", is(title)))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
}
和配置文件:
package com.myproject.config;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(jackson2HttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
Jackson2ObjectMapperBuilder builder = this.jacksonBuilder();
converter.setObjectMapper(builder.build());
return converter;
}
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
return builder;
}
}
将我生成的 WAR 文件部署到 Tomcat 7 in XAMPP 表明命名策略使用正确。我相信它之所以如此工作,是因为在独立设置中,除非另有说明,否则始终使用一组默认的消息转换器。这可以在 StandAloneMockMvcBuilder.java(版本 4.1.6,\org\springframework\test\web\servlet\setup\StandaloneMockMvcBuilder.java
)中的 setMessageConverters()
函数的注释中看到:
/**
* Set the message converters to use in argument resolvers and in return value
* handlers, which support reading and/or writing to the body of the request
* and response. If no message converters are added to the list, a default
* list of converters is added instead.
*/
public StandaloneMockMvcBuilder setMessageConverters(HttpMessageConverter<?>...messageConverters) {
this.messageConverters = Arrays.asList(messageConverters);
return this;
}
因此,如果 MockMvc 在构建 MockMvc 期间未明确告知对消息转换器的更改,它将不会使用这些更改。
或者你可以
MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new
MappingJackson2HttpMessageConverter();
mappingJackson2HttpMessageConverter.setObjectMapper( new ObjectMapper().setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) );
mockMvc = MockMvcBuilders.standaloneSetup(attributionController).setMessageConverters(
mappingJackson2HttpMessageConverter ).build();
使用 Spring Boot 1.5.1 我可以做到:
@RunWith(SpringRunner.class)
@AutoConfigureJsonTesters
@JsonTest
public class JsonTest {
@Autowired
ObjectMapper objectMapper;
}
以与运行时相同的方式访问配置的 ObjectMapper。
我的runtime jackson是这样配置的:
@Configuration
public class JacksonConfiguration {
@Autowired
Environment environment;
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomizer() {
return builder -> {
builder.locale(new Locale("sv", "SE"));
if (JacksonConfiguration.this.environment == null
|| !JacksonConfiguration.this.environment.acceptsProfiles("docker")) {
builder.indentOutput(true);
}
final Jdk8Module jdk8Module = new Jdk8Module();
final ProblemModule problemModule = new ProblemModule();
final JavaTimeModule javaTimeModule = new JavaTimeModule();
final Module[] modules = new Module[] { jdk8Module, problemModule,
javaTimeModule };
builder.modulesToInstall(modules);
};
}
}
在 Spring 引导中,当对控制器层 (@WebMvcTest) 进行单元测试时,您可以访问对象映射器,因此您可以在测试用例之前对其进行修改:
@Autowired
private ObjectMapper objectMapper;
@Before
public void init(){
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}