让我的 Spring 测试切片扫描单个类而不是整个包

Joã*_*tos 5 java testing spring-boot

我被要求为现有的 SpringBoot 项目创建集成测试,该项目的组织不像我希望的那样模块化。例如,有一个包生成与所有服务关联的所有存储库。这成为一个问题,对我来说,当我试图创建一个@WebMvcTest测试片,因为当我使用@ComponentScan@EnableJpaRepositories@EntityScan看我的目标类它结束了扫描共享同一包中的所有其他不必要的。

由于更改项目结构并不是我自己可以做出的决定,我的问题是是否有可能让我的测试扫描选择一个特定的类并忽略同一个包中的所有其他类?

感谢您的关注

Joã*_*tos 3

感谢约瑟夫的回答和这些,我终于能够实现所有必需的过滤:

组件和服务可以配置为产生过滤器,因此我们可以指定目标服务和控制器,并同时排除其他所有内容:

 @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)

        }
)
Run Code Online (Sandbox Code Playgroud)

存储库。这不太可能适用于存储库,但幸运的是,它们@EnableJpaRepositories支持相同类型的过滤器:

  @EnableJpaRepositories(
       basePackageClasses = {
            MyTargetRepository.class
       },
       includeFilters = {
            @ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = MyTargetRepository.class)
       }
  )
Run Code Online (Sandbox Code Playgroud)

实体。这部分比较棘手,因为 @EntityScan 不支持这些过滤器。尽管实体不引用 Spring bean,但我更喜欢仅加载测试所需的实体。我无法找到支持过滤的实体的任何注释,但我们可以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());
                }
            }

        }

    }


}
Run Code Online (Sandbox Code Playgroud)