我被要求为一个现有的SpringBoot项目创建集成测试,该项目的组织结构没有我所希望的模块化。例如,有一个软件包产生与所有服务关联的所有存储库。这成为一个问题,对我来说,当我试图创建一个@WebMvcTest测试片,因为当我使用@ComponentScan,@EnableJpaRepositories,@EntityScan看我的目标类它结束了扫描共享同一包中的所有其他不必要的。
@WebMvcTest
@ComponentScan
@EnableJpaRepositories
@EntityScan
由于更改项目结构并不是我自己一个人真正的决定,因此我的问题是,是否有可能让我的测试扫描选择一个特定的类而不理会同一软件包中的所有其他类?
感谢您的关注
可以将 组件和服务配置为产生过滤器,因此我们可以指定目标服务和控制器,并同时排除其他所有内容:
@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,但我更喜欢仅加载测试所需的实体。我找不到支持过滤的实体的任何注释,但是我们可以使用中的来PersistenceUnitPostProcessor以编程方式过滤它们EntityManagerFactory。这是我的完整解决方案:
PersistenceUnitPostProcessor
EntityManagerFactory
//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()); } } } } }