@WebMvcTest не исключает и загружает компоненты, помеченные как @Repository

#spring #spring-test #spring-boot-test

#spring #spring-test #spring-boot-test

Вопрос:

У меня есть, у @RestController которого есть только одна зависимость в поле @Autowire , которая является зависимостью @component, в этом определении класса компонента есть несколько автоматически подключаемых полей, которые являются @service, и у этих служб есть несколько @repositories.

Во всем этом потоке у меня есть kafka, Quartz, Cassandra и DB2, поэтому, когда я создавал модульный тестовый пример для своего контроллера, я не хочу настраивать все приложение. поэтому я решил использовать @WebMvcTest и использовал @MockBean для моей единственной зависимости от класса контроллера.

Но мой тест выдает и исключение, потому что он пытается создать компонент Dao, который помечен как @repository .

 @ActiveProfiles("test")
@WebMvcTest(controllers = MyControllerTest .class)
class MyControllerTest {

    @MockBean
    MyControllerDependency dependency;

    @Autowired
    MockMvc mockMvc;

    @Test
    void test_something() throws Exception {
       assert(true);
    }
}
  

Вот упрощенная версия кода

 @Component
class MyControllerDependency { 
    @AutoiWired
    MyCustomService service;
}

@Service
class MyCustomService{

   @Autowired
   MyCustomDao dao;
}

@Repository
class MyCustomDao{
    @Autowired
    private JdbcTemplate template;
}
  

В тесте я получаю следующее исключение.

 Exception

***************************
APPLICATION FAILED TO START
***************************

Description:

Field template in com.....MyCustomDao`  required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.
  

Вопрос в том, когда я использую @WebMvcTest slice и уже издеваюсь над единственной требуемой зависимостью MyControllerDependency , тогда почему spring test context пытается загрузить MyCustomDao , который аннотируется как @Repository .

Я могу проводить интеграционное тестирование с SpringbootTest помощью amp; AutoconfigureMockMVC , но для написания теста Junit только для контроллера мне нужно использовать WebMvcTest slice . что создает проблему.

Ответ №1:

Я столкнулся с аналогичной проблемой, когда я хочу протестировать только свой контроллер с помощью @WebMvcTest, но контекст spring пытался создать независимые компоненты spring и терпел неудачу, как показано ниже.

Не удалось загрузить ApplicationContext java.lang.Исключение IllegalStateException: не удалось загрузить ApplicationContext, вызванный: org.springframework.beans.factory.Исключение UnsatisfiedDependencyException: ошибка при создании компонента с именем ‘TestController’, определенным в файле…

Решение: загрузите только контроллер, который вы тестируете, для примера, подобного @ContextConfiguration(classes = DemoController.class) . Кроме того, ниже приведен полный пример

 @WebMvcTest
@ContextConfiguration(classes = DemoController.class)  
public class DemoControllerTest {    
@Autowired
MockMvc mockMvc;

@MockBean
DemoService demoService;

@Test
public void testGetAllProductCodes_withOutData() throws Exception {
    
when(productCodeService.getAllProductCodes()).thenReturn(new ArrayList<ProductCodes>());
        mockMvc.perform(MockMvcRequestBuilders.get("/services/productCodes")).andExpect(MockMvcResultMatchers.status().isNoContent());
    }
}
  

}

Комментарии:

1. Спасибо, я потерял доступ к этому репозиторию кода из-за смены клиента 🙂 Но я протестирую аналогичный сценарий и сообщу вам, как это происходит… Спасибо за обновление, хотя

2. почему в этом чертовом мире @ContextConfiguration(classes = DemoController.class) работает и @WebMvcTest(controllers = DemoController.class) не работает, мне не понять. Это было действительно полезно. Спасибо.

3. @Eugene Вы выяснили, почему ContextConfiguration работает, а WebMvcTest — нет?

4. @JustvanderVeeken Я этого не делал, извини, приятель.

5. @JustvanderVeeken @Eugene Нашел здесь источник spring и отладку. В классе / методе SpringBootTestContextBootstrapper.buildTestContext , если вы не используете ContextConfiguration then в TestContext instance, поле mergedContextConfiguration.classes будет указывать на основной класс вашего приложения вместо вашего контроллера, и, таким образом, он будет использовать настройку вашего основного класса (по крайней мере, в некоторой степени).

Ответ №2:

Есть ли у вас какие @ComponentScan("...") -либо активные аннотации на вашем @SpringBootApplication ?

Как описано в справочной документации по загрузке Spring:

Другим источником путаницы является сканирование пути к классу. Предположим, что, хотя вы структурировали свой код разумным образом, вам необходимо отсканировать дополнительный пакет. Ваше приложение может выглядеть следующим образом:

 @SpringBootApplication
@ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {

    // ...

}
  

Это эффективно переопределяет директиву сканирования компонентов по умолчанию с побочным эффектом сканирования этих двух пакетов независимо от выбранного вами фрагмента. Например, @DataJpaTest, похоже, внезапно сканирует компоненты и пользовательские конфигурации вашего приложения. Опять же, перенос пользовательской директивы в отдельный класс — хороший способ исправить эту проблему.

Одним из решений является создание отдельного @Configuration элемента с @ComponentScan комментариями . При создании @WebMvcTest конфигурации (и ее проверка компонентов игнорируется).

 @Configuration
@ComponentScan("com.example.another")
public class DbConfig {
}
  

Ответ №3:

Обычно это происходит, когда у вас есть явная аннотация @ComponentScan в главном классе приложения spring boot.

Аннотация @ComponentScan подавляет механизм сканирования компонентов по умолчанию, который выполняется с помощью @Webmvctest, где он сканирует иерархию пакетов и применяет фильтры исключения, чтобы найти только контроллер и связанные с ним классы.

Комментарии:

1. Спасибо @Kumar_V, да, в моей внутренней корпоративной среде есть класс приложений микросервисов по умолчанию, который расширяют все приложения микросервисов,,,, и в этом родительском классе определено сканирование компонентов для com.company.domain. и я не могу его удалить. Есть ли еще обходной путь для использования тестирования среза?

Ответ №4:

Когда вы издеваетесь над своим компонентом, используя @MockBean аннотацию, вы должны определить, что этот издеваемый компонент должен делать при вызове его метода, обычно вы делаете это с помощью when in Mockito. В вашем случае это можно сделать следующим образом:

 @ActiveProfiles("test")
@WebMvcTest
class MyControllerTest {

    @MockBean
    MyControllerDependency dependency;

    @Autowired
    MockMvc mockMvc;

    @Test
    void test_something() throws Exception {
        when(dependency.sample()).thenReturn("Hello, Mock");
        mockMvc.perform(get("/api/test/restpoint")
                .accept(MediaType.APPLICATION_JSON))
               .andDo(print())
               .andExpect(status().isOk());
    }
}
  

В этой строке:

 when(dependency.sample()).thenReturn("Hello, Mock");
  

Вместо dependency.sample() этого вы должны поместить любой метод MyControllerDependency класса, который ваш контроллер вызывает при отправке GET запроса в /api/test/restpoint path, и вместе с thenReturn("Hello, Mock") вами определить, каков макет этого метода (когда он вызывается вашим контроллером в вашем модульном тестировании).

Комментарии:

1. Спасибо за ответ, но это совсем не проблема. ради вас я обновлю строку MockBehaviour… проблема, которую я запросил, связана с запуском контекста spring в тестовом случае … поэтому, даже если я удалю весь код в методе тестирования и выполню assert (true), он все равно завершится неудачей

2. Я обновил вопрос, чтобы избежать отвлечения внимания из-за множественной информации