SpringBootTest中无法注入MapStruct Mapper Bean的轻量测试方案咨询
我来帮你搞定这个问题~你遇到的核心问题是:MapStruct的Mapper接口是编译时生成实现类的,而你在测试时只给@SpringBootTest指定了MyEntityMapper.class,Spring容器没法识别到这个接口对应的实现类(也就是MapStruct自动生成的MyEntityMapperImpl),所以找不到可注入的Bean。
下面给你几个轻量化的解决方案,不用加载全量Spring上下文:
方案1:用MapStruct官方的测试注解(最推荐,完全脱离Spring上下文)
如果你的MapStruct版本是1.5及以上,可以直接用MapStruct提供的@MapperTest注解,专门用来测试Mapper,不需要启动Spring容器,速度极快:
import org.mapstruct.testing.MapperTest; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @MapperTest class MyEntityMapperTest { // 直接获取Mapper实例,MapStruct会自动处理生成的实现类 private MyEntityMapper underTest = Mappers.getMapper(MyEntityMapper.class); @Test @DisplayName("Entity mapped when input provided") void test_toMyEntity_returnsMappedEntity_whenInputProvided() { MyEntity result = underTest.toMyEntity(MyInputRepository.basicInput()); assertThat(result).isNotNull(); // 其他断言逻辑 } }
这个方案完全不需要Spring的依赖注入,测试启动速度几乎是瞬时的。
方案2:用精简的Spring测试配置(保留Spring容器但不加载全上下文)
如果你一定要用Spring的依赖注入,可以创建一个测试专用的配置类,让Spring扫描到MapStruct生成的实现类:
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = "com.example.mapper") // 替换成你的Mapper所在包路径 public class MapperTestConfig { }
然后修改测试类的@SpringBootTest配置,加载这个测试配置类即可:
@SpringBootTest(classes = MapperTestConfig.class) class MyEntityMapperTest { @Autowired private MyEntityMapper underTest; // 测试逻辑不变 }
这样Spring会扫描到MapStruct生成的MyEntityMapperImpl(因为你给Mapper指定了componentModel = SPRING,所以生成的实现类会带有@Component注解),就能成功注入了。
方案3:直接实例化生成的实现类(最直接,无Spring依赖)
MapStruct默认会给Mapper接口生成一个名为[接口名]Impl的实现类,你可以直接在测试里new这个类,完全不用Spring:
class MyEntityMapperTest { // 直接实例化MapStruct生成的实现类 private MyEntityMapper underTest = new MyEntityMapperImpl(); @Test @DisplayName("Entity mapped when input provided") void test_toMyEntity_returnsMappedEntity_whenInputProvided() { MyEntity result = underTest.toMyEntity(MyInputRepository.basicInput()); assertThat(result).isNotNull(); // 其他断言逻辑 } }
这个方案最简单,适合快速编写测试,缺点是如果以后修改了Mapper的生成规则(比如自定义实现类名),需要同步修改测试代码。
为什么原来的写法不行?
你之前用@SpringBootTest(classes = MyEntityMapper.class),Spring只会把这个接口本身加入容器,但MapStruct的Mapper接口本身不是Bean——真正的Bean是编译时生成的MyEntityMapperImpl,而这个实现类不在你指定的classes列表里,Spring也不会自动去扫描它,所以就出现了找不到Bean的错误。
内容来源于stack exchange

