programing

테스트에 사용자 지정된 ObjectMapper가 사용되지 않음

muds 2023. 8. 19. 10:56
반응형

테스트에 사용자 지정된 ObjectMapper가 사용되지 않음

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"}

구현된 웹 응용 프로그램이 있습니다.패키지를 검색하는 이니셜라이저:

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콩으로서, 그리고 설정.ObjectMapper헛되이

어떤 도움이라도 주시면 대단히 감사하겠습니다!다른 필요한 파일이 있으면 알려주시기 바랍니다.

감사합니다!

에디트: 좀 더 파다가 이걸 발견했습니다.링크에 작성자가 추가됩니다.setMessageConverters()MockMvc를 구축하기 전에 작성자를 위해 작동합니다.동일한 작업을 수행하는 것이 저에게도 효과가 있었습니다. 하지만 아직 저장소가 플러시되지 않았기 때문에 운영 환경에서 모든 것이 제대로 작동할지 확신할 수 없습니다.제가 알게 되면 답변을 제출하겠습니다.

EDIT 2: 답변을 참조하십시오.

저는 왜 이것이 그렇게 작동하는지 이해하려고 했습니다.다시 한 번 말씀드리지만, MockMvc가 독립 실행형으로 생성되고 있다고 가정할 때 사용자 정의된 ObjectMapper가 테스트에서 작동하도록 하는 프로세스는 다음과 같습니다.

  1. 작성WebConfig확장되는 클래스WebMvcConfigurerAdapter.
  2. 에서WebConfig클래스, 새 만들기@Bean그것은 a를 반환합니다.MappingJackson2HttpMessageConverter.이것.MappingJackson2HttpMessageConverter원하는 변경 사항이 적용되었습니다(나의 경우, 그것은 그것을 통과하고 있었습니다).Jackson2ObjectMapperBuilder와 함께PropertyNamingStrategy로 설정한.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES.)
  3. 또한WebConfig학급,@Override configureMessageConverters()추가합니다.MappingJackson2HttpMessageConverter(2)에서 메시지 변환기 목록으로 이동합니다.
  4. 테스트 파일에 다음을 추가합니다.@ContextConfiguration(classes = { WebConfig.class }) 결과를 @Bean.
  5. 사용하다@Autowired주사하고 접근하기 위해@Bean(된 바와 같다.
  6. 에의 에서.MockMvc을 사용합니다..setMessageConverters()한 주 방 법 및 전 달 입 된 전 달 ▁and ▁it ▁method ▁the 및 ▁pass주 입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() 4 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가 메시지 변환기에 대한 변경사항을 명시적으로 알리지 않으면 변경사항을 사용하지 않습니다.

아니면 할 수 있습니다.

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에 액세스합니다.

런타임 잭슨은 다음과 같이 구성됩니다.

@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 boot에서 장치가 컨트롤러 계층을 테스트할 때(@WebMvcTest) 개체 매퍼에 액세스할 수 있으므로 테스트 사례 전에 개체 매퍼를 수정할 수 있습니다.

@Autowired
private ObjectMapper objectMapper;

@Before
public void init(){
    objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}

언급URL : https://stackoverflow.com/questions/31883657/customized-objectmapper-not-used-in-test

반응형