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

发布于 2021-02-02 11:44:18

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

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

感谢您的关注

关注者
0
被浏览
59
1 个回答
  • 面试哥
    面试哥 2021-02-02
    为面试而生,有面试问题,就找面试哥。

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

     @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。这是我的完整解决方案:

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


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看