简体   繁体   English

带有 MapStruct 的 @SpringBootTest 需要 Impl

[英]@SpringBootTest with MapStruct requires Impl

I have following test:我有以下测试:

@SpringBootTest(classes = {SomeService.class, DtoMapperImpl.class})
class SomeServiceTest {

And following mapper:和以下映射器:

@Mapper(componentModel = "spring")
public interface DtoMapper {
    EntityDto toDto(Entity entity);
}

I'm not changing packages (this means DtoMapperImpl is in the same package as DtoMapper )我没有更改包(这意味着DtoMapperImpl与 DtoMapper 在同一个package中)

As soon as I change Impl to interface my test fails:一旦我将 Impl 更改为接口,我的测试就会失败:

@SpringBootTest(classes = {SomeService.class, DtoMapper.class})
class SomeServiceTest {

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someService': Unsatisfied dependency expressed through constructor parameter 2; Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'someService': Unsatisfied dependency expressed through constructor parameter 2 ; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'DtoMapper' available: expected at least 1 bean which qualifies as autowire candidate.嵌套异常是 org.springframework.beans.factory.NoSuchBeanDefinitionException:没有可用的“DtoMapper”类型的合格 bean:预计至少有 1 个 bean 有资格作为自动装配候选者。 Dependency annotations: {}依赖注解:{}

Can you please advise best way solving this?你能建议解决这个问题的最佳方法吗? I'm on MapStruct 1.3.1.Final我在 MapStruct 1.3.1.Final

Create following configuration (should point where mappers are):创建以下配置(应该指向映射器所在的位置):

@TestConfiguration
@ComponentScan("some.package.mapper")
public class MappersConfig {
}

And modify slice:并修改切片:

@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {

The problem actually has nothing to do with MapStruct, but rather to how SpringBootTest#classes is used.问题实际上与 MapStruct 无关,而是与SpringBootTest#classes的使用方式有关。

The classes in SpringBootTest is meant to provide your components that should be used to load in the test. SpringBootTestclasses旨在提供应该用于加载测试的组件。

From the JavaDoc:来自 JavaDoc:

The component classes to use for loading an ApplicationContext .用于加载ApplicationContext的组件类。 Can also be specified using @ContextConfiguration(classes=...) .也可以使用@ContextConfiguration(classes=...)指定。 If no explicit classes are defined the test will look for nested @Configuration classes, before falling back to a @SpringBootConfiguration search.如果没有定义明确的类,测试将在回@SpringBootConfiguration搜索之前查找嵌套的@Configuration类。 Returns: the component classes used to load the application context返回: 用于加载应用程序上下文的组件类

In your case you have 2 classes:在您的情况下,您有 2 个课程:

  • SomeService - which I assume is a class annotated with @Service and Spring will correctly load it SomeService - 我假设它是一个用@Service注释的类,Spring 会正确加载它
  • DtoMapper - this is the MapStruct mapper which is an interface and it isn't a component. DtoMapper - 这是 MapStruct 映射器,它是一个接口,而不是一个组件。 The component which you want for your tests is DtoMapperImpl您需要用于测试的组件是DtoMapperImpl

You have several options to fix this:您有多种选择可以解决此问题:

Use the Impl class使用 Impl 类

You can use the DtoMapperImpl (the Spring Component class) in your SpringBootTest#classes , your test will then load the correct component您可以在SpringBootTest#classes使用DtoMapperImpl (Spring Component 类),然后您的测试将加载正确的组件

Use a custom configuration class that will component scan your mappers使用自定义配置类来组件扫描您的映射器

@TestConfiguration
@ComponentScan("com.example.mapper")
public class MappersConfig {

}

And then use this in your SpringBootTest#classes .然后在您的SpringBootTest#classes使用它。 Eg例如

@SpringBootTest(classes = {SomeService.class, MappersConfig.class})
class SomeServiceTest {
   ...
}

I would suggest a little improvement for accepted answer so that you don't have to write package name as hard-coded string, but use ComponentScan basePackageClasses instead:我建议对已接受的答案进行一些改进,这样您就不必将 package 名称写为硬编码字符串,而是改用 ComponentScan basePackageClasses:

@TestConfiguration
// @ComponentScan("some.package.mapper")
@ComponentScan(basePackageClasses = DtoMapper.class)
public class MappersConfig {
}

But there is an obvious drawback in this (as well as accepted answer) approach: the whole package will be scanned which may contain undesired classes.但是这种(以及接受的答案)方法有一个明显的缺点:整个 package 将被扫描,其中可能包含不需要的类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM