#java #spring-boot-test #mapstruct
#java #spring-boot-test #mapstruct
Вопрос:
У меня есть следующий тест:
@SpringBootTest(classes = {SomeService.class, DtoMapperImpl.class})
class SomeServiceTest {
И следующий mapper:
@Mapper(componentModel = "spring")
public interface DtoMapper {
EntityDto toDto(Entity entity);
}
Я не меняю пакеты (это означает, что DtoMapperImpl находится в том же пакете, что и DtoMapper)
Как только я меняю Impl на интерфейс, мой тест завершается неудачей:
@SpringBootTest(classes = {SomeService.class, DtoMapper.class})
class SomeServiceTest {
Вызвано:
org.springframework.beans.factory.Исключение UnsatisfiedDependencyException:
Ошибка при создании компонента с именем ‘SomeService’: неудовлетворенная зависимость
, выраженная через параметр конструктора 2; вложенным исключением является
org.springframework.beans.factory.Исключение NoSuchBeanDefinitionException: отсутствует
соответствующий компонент типа ‘DtoMapper’: ожидается по крайней мере 1
компонент, который квалифицируется как кандидат на автоматическое подключение. Аннотации зависимостей: {}
Не могли бы вы посоветовать лучший способ решения этой проблемы? Я нахожусь на MapStruct 1.3.1.Final
Комментарии:
1. Потому что вы используете
@SpringBootTest
неправильно. То есть для интеграционных тестов spring boot вы хотите протестировать свой сервис, а затем не использовать@SpringBootTest
его для этого.2. Извините, это не отвечает на мой вопрос
3. Как указано, вы используете неправильный инструмент для задания или в данном случае используете инструмент неправильно. Вы либо пишете полный интеграционный тест (а затем удаляете
classes
часть), ЛИБО пишете простой модульный тест для своего сервиса и вручную вводите mapper, получая его из самого MapStruct.4. Извините, просто хотел отметить, что ваши комментарии не по теме в этом вопросе. Если вы хотите обсудить идеологию, как писать интеграционные тесты, я буду рад сделать это вне этого вопроса. Моды, пожалуйста, очистите
5. Речь идет не об идеологии, а об использовании неправильного инструмента для вашего теста, что я и пытаюсь объяснить.
Ответ №1:
Проблема на самом деле не имеет ничего общего с MapStruct, а скорее с тем, как SpringBootTest#classes
он используется.
classes
In SpringBootTest
предназначен для предоставления ваших компонентов, которые должны использоваться для загрузки в тесте.
Из JavaDoc:
Классы компонентов, используемые для загрузки
ApplicationContext
. Также может быть указано с помощью@ContextConfiguration(classes=...)
. Если явные классы не определены, тест будет искать вложенные@Configuration
классы, прежде чем вернуться к@SpringBootConfiguration
поиску. Возвращает: классы компонентов, используемые для загрузки контекста приложения
В вашем случае у вас есть 2 класса:
SomeService
— я предполагаю, что это класс, аннотированный,@Service
и Spring правильно загрузит егоDtoMapper
— это MapStruct mapper, который является интерфейсом, а не компонентом. Компонент, который вы хотите использовать для своих тестов, являетсяDtoMapperImpl
У вас есть несколько вариантов, чтобы исправить это:
Используйте класс Impl
Вы можете использовать DtoMapperImpl
(класс Spring Component) в своем SpringBootTest#classes
тесте, после чего ваш тест загрузит правильный компонент
Используйте пользовательский класс конфигурации, который будет сканировать ваши картографы
@TestConfiguration
@ComponentScan("com.example.mapper")
public class MappersConfig {
}
А затем используйте это в своем SpringBootTest#classes
. Например.
@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
...
}
Комментарии:
1. Спасибо, это то, что я опубликовал ранее. Не могли бы вы скопировать мой пример кода во 2-м варианте, и я отмечу это как ответ?
Ответ №2:
Создайте следующую конфигурацию (должна указывать, где находятся картографы):
@TestConfiguration
@ComponentScan("some.package.mapper")
public class MappersConfig {
}
И изменить фрагмент:
@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
Ответ №3:
Я бы предложил небольшое улучшение для принятого ответа, чтобы вам не нужно было записывать имя пакета в виде жестко заданной строки, но вместо этого используйте ComponentScan basePackageClasses:
@TestConfiguration
// @ComponentScan("some.package.mapper")
@ComponentScan(basePackageClasses = DtoMapper.class)
public class MappersConfig {
}
Но в этом (а также в принятом ответе) подходе есть очевидный недостаток: будет проверен весь пакет, который может содержать нежелательные классы.