我被要求为现有的 SpringBoot 项目创建集成测试,该项目的组织并不像我希望的那样模块化。例如,有一个包生成与所有服务关联的所有存储库。当我尝试创建 @WebMvcTest
测试切片时,这对我来说成为了一个问题,因为当我使用 @ComponentScan
、@EnableJpaRepositories
、@EntityScan
来读取我的目标类,它最终会扫描共享同一包的所有其他不必要的类。
由于更改项目结构实际上并不是我可以自己做出的决定,因此我的问题是是否可以让我的测试扫描选择一个特定的类并忽略同一包中的所有其他类?
感谢您的关注
最佳答案
感谢约瑟夫的回答和这些,我终于能够实现所有必需的过滤:
组件和服务可以配置为产生过滤器,因此我们可以指定目标服务和 Controller ,并同时排除其他所有内容:
@ComponentScan(
basePackageClasses = {
MyTargetService.class,
MyTargetController.class
},
useDefaultFilters = false,
includeFilters = {
@ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = MyTargetService.class),
@ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = MyTargetController.class)
}
)
存储库。这不太可能适用于存储库,但幸运的是,@EnableJpaRepositories
支持相同类型的过滤器:
@EnableJpaRepositories(
basePackageClasses = {
MyTargetRepository.class
},
includeFilters = {
@ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = MyTargetRepository.class)
}
)
实体。这部分比较棘手,因为@EntityScan不支持这些过滤器。尽管实体不引用 Spring bean,但我更喜欢仅加载测试所需的实体。我无法找到支持过滤的实体的任何注释,但我们可以使用 EntityManagerFactory
中的 PersistenceUnitPostProcessor
以编程方式过滤它们。这是我的完整解决方案:
//add also the filtered @ComponentScan and @EnableJpaRepositories annotations here
@Configuration
public class MyConfig {
//here we specify the packages of our target entities
private static String[] MODEL_PACKAGES = {
"com.full.path.to.entity.package1",
"com.full.path.to.entity.package2"
};
//here we specify our target entities
private static Set<String> TARGET_ENTITIES = new HashSet<>(Arrays.asList(
"com.full.path.to.entity.package1.MyTargetEntity1",
"com.full.path.to.entity.package2.MyTargetEntity2"
));
@Bean
public DataSource getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
return builder.setType(EmbeddedDatabaseType.H2).build();
}
@Bean
public EntityManagerFactory entityManagerFactory() {
ReflectionsPersistenceUnitPostProcessor reflectionsPersistenceUnitPostProcessor = new ReflectionsPersistenceUnitPostProcessor();
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPackagesToScan(MODEL_PACKAGES);
factory.setDataSource(getDataSource());
factory.setPersistenceUnitPostProcessors(reflectionsPersistenceUnitPostProcessor);
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
return txManager;
}
public class ReflectionsPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
@Override
public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
Reflections r = new Reflections("", new TypeAnnotationsScanner());
Set<Class<?>> entityClasses = r.getTypesAnnotatedWith(Entity.class, true);
Set<Class<?>> mappedSuperClasses = r.getTypesAnnotatedWith(MappedSuperclass.class, true);
pui.getManagedClassNames().clear(); //here we remove all entities
//here we add only the ones we are targeting
for (Class<?> clzz : mappedSuperClasses) {
if (TARGET_ENTITIES.contains(clzz.getName())) {
pui.addManagedClassName(clzz.getName());
}
}
for (Class<?> clzz : entityClasses) {
if (TARGET_ENTITIES.contains(clzz.getName())) {
pui.addManagedClassName(clzz.getName());
}
}
}
}
}
关于java - 让我的 Spring 测试切片扫描单个类而不是整个包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60260228/