Интеграционный тест SpringBoot - TestRestTemplate не достигает контроллера и получает 404 вместо 200

Я пытался немного поиграть с интеграционным тестом в Springboot, поэтому я создал образец теста, используя аннотацию @SpringBootTest. Мой образец теста:

     @RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
     @ContextConfiguration(classes = IntegrationTestConfig.class)
        public class WeatherForCityIT {


            @Autowired
            private TestRestTemplate restTemplate;



            @Test
            public void getWeatherForExistingCity() throws Exception {

                String existingCity = "London";

                ResponseEntity<String> responseEntity = restTemplate.getForEntity("/weather/{cityName}",String.class,existingCity.toString());
                Assertions.assertThat(responseEntity).isNotNull();


            }
        }

И иметь следующий класс контроллера

@RestController
@RequestMapping("/weather")
public class ChartController {

    private WeatherForecastAPI weatherForecastAPI;

    @Autowired
    public void setWeatherForecastAPI(WeatherForecastAPI weatherForecastAPI) {
        this.weatherForecastAPI = weatherForecastAPI;
    }

    @GetMapping("/{cityName}")
    public List<WeatherForecastDTO> get5daysForecast(@PathVariable String cityName) {

        weatherForecastAPI.getWeatherForecastByCity(cityWithCountryCode.toString());            
    }

}

К сожалению, в теле ответа я получаю сообщение 404 Not Found. В режиме отладки я вижу, что он никогда не достигает определенного контроллера. Я что-то упустил с точки зрения конфигурации? Я также пытался использовать MockMvc:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ContextConfiguration(classes = IntegrationTestConfig.class)
public class WeatherForCityIT {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void getWeatherForExistingCity() throws Exception {

        String existingCity = "London";

        restTemplate.getForEntity("/weather/{cityName}",String.class,existingCity);

        mockMvc.perform(get("/weather/" + existingCity))
               .andDo(print())
               .andExpect(MockMvcResultMatchers.status().isOk());
    }
}

но тоже безуспешно (опять 404 вместо 202).

ОТРЕДАКТИРОВАНО

Класс конфигурации выглядит следующим образом:

@Configuration
@EnableAutoConfiguration
public class IntegrationTestConfig {

    @Bean
    public com.jayway.jsonpath.Configuration configuration() {

        return com.jayway.jsonpath.Configuration.builder()
                                                .jsonProvider(new JacksonJsonProvider())
                                                .mappingProvider(new JacksonMappingProvider())
                                                .options(EnumSet.noneOf(Option.class))
                                                .build();
    }

}

person Artur Skrzydło    schedule 29.11.2017    source источник
comment
Глядя на ваш код, сопоставления запросов выглядят нормально. Что вы можете сделать, так это проверить доступные сопоставления в вашем тестовом контексте. Автоподключение RequestMappingHandlerMapping к тестовому классу WeatherForCityIT и доступ к сопоставлениям с помощью RequestMappingHandlerMapping.getHandlerMethods().keySet();. Проверьте, есть ли в этом списке путь /weather/{cityName}.   -  person Igor    schedule 30.11.2017
comment
Спасибо, Игорь, я проверил это и получил No qualifying bean of type 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping' available, это приводит меня к этому, я не вставил сюда полный код. Я пропускаю @ConfigurationContext , указывающий на пустой класс конфигурации. Когда я удаляю это, то все работает нормально. Почему это ? Почему указание класса конфигурации вызывает эту ошибку?   -  person Artur Skrzydło    schedule 30.11.2017
comment
С помощью @ContextConfiguration вы указываете, какие @Configuration классы нужно загрузить для настройки вашего ApplicationContext в ваших тестах. Обычно вам это не нужно при тестировании приложений Spring Boot. @SpringBootTest автоматически выполнит поиск вашей основной конфигурации в классе с аннотацией @SpringBootApplication. Если вам нужно настроить вашу основную конфигурацию, лучше использовать класс @TestConfiguration. Вы можете узнать больше здесь.   -  person Igor    schedule 30.11.2017
comment
Хорошо, но как быть с не вложенной конфигурацией. Этот класс конфигурации является общим для многих других тестов, поэтому я не хочу копировать и вставлять его в другие классы. Поскольку @TestConfiguration для вложенной конфигурации   -  person Artur Skrzydło    schedule 30.11.2017
comment
Попробуйте аннотировать свой IntegrationTestConfig с помощью @TestConfiguration вместо @Configuration.   -  person Igor    schedule 30.11.2017
comment
Нужно ли мне также каким-то особым образом определять использование этого класса в моем тестовом классе. Я спрашиваю, потому что я пытался сделать это, но безуспешно   -  person Artur Skrzydło    schedule 30.11.2017
comment
Вы пробовали @SpringBootTest(classes = {IntegrationTestConfig.class}, ...) или @ContextConfiguration(classes = IntegrationTestConfig.class), как в вашем примере кода?   -  person Igor    schedule 30.11.2017
comment
Да, я пробовал оба, но безуспешно. В обоих случаях конфигурация не загружается (исключение Нет доступного подходящего bean-компонента типа «com.jayway.jsonpath.Configuration»: ожидается один соответствующий bean-компонент, но найдено 2: getConfiguration, конфигурация) . Я обновил свой вопрос своим файлом конфигурации   -  person Artur Skrzydło    schedule 30.11.2017


Ответы (1)


Вам не нужно @EnableAutoConfiguration в вашем тестовом классе конфигурации. Поэтому IntegrationTestConfig должен выглядеть так:

@TestConfiguration
public class IntegrationTestConfig {

    @Bean
    public com.jayway.jsonpath.Configuration configuration() {

        return com.jayway.jsonpath.Configuration.builder()
                .jsonProvider(new JacksonJsonProvider())
                .mappingProvider(new JacksonMappingProvider())
                .options(EnumSet.noneOf(Option.class))
                .build();
    }
}

Ваш WeatherForCityIT должен оставаться таким же, как в примере кода:

@RunWith(SpringRunner.class)
     @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
     @ContextConfiguration(classes = IntegrationTestConfig.class)
public class WeatherForCityIT {
     // your code here...
}

Что касается сообщения об исключении, которое вы получаете:

Нет подходящего компонента типа «com.jayway.jsonpath.Configuration»: ожидается один соответствующий компонент, но найдено 2: getConfiguration,configuration)

Из сообщения об ошибке вы знаете, что в вашем контексте есть 2 bean-компонента одного типа (com.jayway.jsonpath.Configuration):

  1. Бин с именем getConfiguration
  2. Бин с именем configuration

Компонент configuration определяется в вашем IntegrationTestConfig, другой компонент getConfiguration определяется в одном из ваших классов конфигурации. Где-то в вашем приложении вы автоматически подключаете bean-компонент com.jayway.jsonpath.Configuration по типу. Поскольку у вас есть 2 bean-компонента этого типа, Spring жалуется на исключение.

Вам нужны обе эти фасоли? Если нет, удалите один из bean-компонентов. В противном случае рассмотрите возможность использования @Qualifier при автоподключении bean-компонентов.

person Igor    schedule 30.11.2017
comment
Я думаю, что это правильное решение для моей проблемы. Подводя итог: @ContextConfiguration (classes = IntegrationTestConfig.class) должен ссылаться на класс @TestConfiguration, иначе класс @Configuration переопределит мою основную конфигурацию из приложения. Теперь @Qualifier не лучший в моем случае - это интеграционный тест, поэтому автомонтаж происходит в бизнес-методе, а не в методе тестирования. Но с профилированием я могу добиться того же эффекта. Более того, мне не нужно определять @ContextConfiguration, так как это не помогает с проблемой неоднозначности bean-компонентов. Спасибо за помощь @Igor ! - person Artur Skrzydło; 01.12.2017