#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. Я обновил вопрос, чтобы избежать отвлечения внимания из-за множественной информации