当前位置: 首页 > 面试题库 >

测试中未使用的自定义ObjectMapper

颜文昌
2023-03-14
问题内容

我正在使用带有Spring Web服务且没有Spring Boot的Spring Framework版本4.1.6。要学习该框架,我正在编写REST
API并进行测试以确保从命中端点收到的JSON响应正确。具体来说,我想调整ObjectMapperPropertyNamingStrategy‘用下划线小写’命名策略使用。

我正在使用Spring博客上详细介绍的方法创建一个新方法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());
    }
}

但是,这将返回以下响应:

{"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并实现WebMvcConfigurationSupportMappingJackson2HttpMessageConverter作为Bean使用,并设置ObjectMapper为Bean无效。

任何帮助将不胜感激!请让我知道是否需要其他文件。

谢谢!

编辑:
正在做更多的挖掘,发现了这一点。在该链接中,作者setMessageConverters()在构建MockMvc之前附加了它,并且对作者有效。这样做同样对我有用。但是,由于存储库尚未清除,因此我不确定是否所有功能都可以在生产中使用。当我发现时,我将提交答案。

编辑2: 请参阅答案。


问题答案:

我调查了一下为什么这样做会如此。重申一下,让我的自定义ObjectMapper在我的测试中工作的过程(假定MockMvc是作为独立的创建的)如下:

  1. 创建一个WebConfig扩展类WebMvcConfigurerAdapter
  2. WebConfig该类中,创建一个@Bean返回的新值MappingJackson2HttpMessageConverter。这MappingJackson2HttpMessageConverter已应用了所需的更改(在我的情况下,将其传递给Jackson2ObjectMapperBuilder,且PropertyNamingStrategy设置为CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES。)
  3. 同样在WebConfig该类中,@Override configureMessageConverters()并将MappingJackson2HttpMessageConverterfrom(2)添加到消息转换器列表中。
  4. 在测试文件中,添加@ContextConfiguration(classes = { WebConfig.class })注释以通知您的测试@Bean
  5. 使用@Autowired注入和访问的@Bean(2)中所定义。
  6. 在的设置中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文件部署到XAMPP中的Tomcat
7表示正确使用了命名策略。我认为这样做是可行的,因为在独立安装中,除非另有说明,否则始终使用默认的消息转换器集。可以setMessageConverters()在StandAloneMockMvcBuilder.java(版本4.1.6,\org\springframework\test\web\servlet\setup\StandaloneMockMvcBuilder.java)中的函数注释中看到:

   /**
     * 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有关消息转换器的更改,它将不会使用这些更改。



 类似资料:
  • 我使用的是Spring Framework4.1.6版本,带有Spring web services,不带Spring Boot。为了学习这个框架,我正在编写一个REST API,并进行测试,以确保从命中一个endpoint收到的JSON响应是正确的。具体地说,我正在尝试调整的以使用“带下划线的小写”命名策略。 我正在使用Spring的博客中详细介绍的方法创建一个新的并将其添加到转换器列表中。具体

  • 我有一个自定义任务定义来运行每个测试具有特殊设置的特定测试文件。我的任务定义如下: 现在,此设置中的一些测试是不可靠的,我尝试再次运行它们,如下所示: 我编写了一个测试类,第一次总是失败,第二次总是成功: 不幸的是,测试只执行一次,整个测试套件失败。我可以使用中所述的自定义规则成功运行测试https://stackoverflow.com/a/55178053/6059889 有没有办法将测试重试

  • 我试图用RobolectRic2.1.1运行单元测试,但我无法让它膨胀自定义布局(例如,ViewPagerIndicator类)。假设这是我的布局: 其结果是: 我的最后一招是尝试使用影子类: 并使用。这再次导致 你们能给我指出正确的方向吗?我没主意了。多谢了。

  • 我正在尝试编写一个单元测试到一个自定义反序列化器,该反序列化器是使用一个带有@了的构造函数来实例化的,并且我的实体标有@JsonDesri的。它在我的集成测试中工作得很好,其中MockMvc会带来Spring serverside。 然而,在调用objectMapper.read值(…)的测试中,使用不带参数的默认构造函数的反序列化器的新实例被实例化。即使 实例化有线版本的反序列化程序,实际调用仍

  • 我正在为我的Spring MVC控制器设置单元测试,并试图利用Spring MVC测试框架。对于控制器中的每个endpoint,我希望确保只有具有指定权限的用户才能访问。我的问题是,在使用mockMvc工具解决这个问题时,我使用了一个自定义用户实现,并获得了类强制转换异常。 对于每个请求,我希望它看起来像这样: 我想以某种方式调整上述语句,以指定我的自定义用户主体。请参见下面Spring的用户方法