让我的Spring测试片扫描单个类而不是整个包 [英] Have my Spring test slice scan a single class instead of the whole package

查看:47
本文介绍了让我的Spring测试片扫描单个类而不是整个包的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我被要求为一个现有的SpringBoot项目创建集成测试,该项目的组织结构没有我所希望的模块化.例如,有一个软件包产生与所有服务关联的所有存储库.当我尝试创建 @WebMvcTest 测试切片时,这成为我的问题,因为当我使用 @ComponentScan @EnableJpaRepositories @EntityScan 读取我的目标类,最终扫描共享同一软件包的所有其他不必要的类.

I was asked to create integration tests for an existing SpringBoot project, whose organisation is not as modular as I would prefer. For example, there is a package yielding all repositories associated with all services. This became a problem for me when I was attempting to create a @WebMvcTest test slice, because when I use @ComponentScan, @EnableJpaRepositories, @EntityScan to read my target classes it ends up scanning all other unnecessary ones that share the same package.

由于更改项目结构并不是我自己一个人真正的决定,因此我的问题是,是否有可能让我的测试扫描选择一个特定的类而不理会同一软件包中的所有其他类?

Since changing the project structure is not really a decision I can make on my own, my question is whether it is possible to have my test scan pick a specific class and disregard all others within the same package?

感谢您的关注

推荐答案

多亏了约瑟夫(Josef)的回答,我终于能够实现所有必需的过滤:

I was finally able to achieve all the required filtering, thanks to Josef's answer and these:

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

Components and Services can be configured to yield filters hence we can specify our target services and controllers and exclude everything else at the same time:

 @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 支持相同类型的过滤器:

Repositories. This is unlikely to work for repositories, but fortunately the @EnableJpaRepositories supports the same type of filters:

  @EnableJpaRepositories(
       basePackageClasses = {
            MyTargetRepository.class
       },
       includeFilters = {
            @ComponentScan.Filter(type = ASSIGNABLE_TYPE, value = MyTargetRepository.class)
       }
  )

实体.这部分比较棘手,因为@EntityScan不支持这些过滤器.尽管这些实体未引用Spring Bean,但我更喜欢仅加载测试所需的实体.我找不到支持过滤的实体的任何注释,但是我们可以使用 EntityManagerFactory 中的 PersistenceUnitPostProcessor 来以编程方式过滤它们.这是我的完整解决方案:

Entities. This part is more tricky because @EntityScan does not support these filters. Although the entities do not reference Spring beans, I prefer loading only the entities necessary for my test. I was not able to find any annotation for entities that supports filtering, but we can filter them programmatically using a PersistenceUnitPostProcessor in our EntityManagerFactory. Here is my full solution:

   //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());
                }
            }

        }

    }


}

这篇关于让我的Spring测试片扫描单个类而不是整个包的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆