Пусть мой тестовый фрагмент Spring отсканирует один класс вместо всего пакета - PullRequest
4 голосов
/ 17 февраля 2020

Меня попросили создать интеграционные тесты для существующего проекта SpringBoot, организация которого не так модульна, как мне бы хотелось. Например, существует пакет, содержащий все репозитории, связанные со всеми сервисами. Это стало проблемой для меня, когда я пытался создать @WebMvcTest тестовый фрагмент, потому что когда я использую @ComponentScan, @EnableJpaRepositories, @EntityScan для чтения моих целевых классов, он заканчивает сканирование всех других ненужных, которые разделяют тот же пакет.

Поскольку изменение структуры проекта на самом деле не является решением, которое я могу принять самостоятельно, мой вопрос заключается в том, можно ли в моем тестовом сканировании выбрать определенный класс c и игнорировать все остальные в пределах такой же пакет?

Спасибо за внимание

Ответы [ 2 ]

0 голосов
/ 18 февраля 2020

Я, наконец, смог добиться всей необходимой фильтрации, благодаря ответу Джозефа и этим:

Компоненты и сервисы можно настроить для получения фильтров, поэтому мы можем указать наши целевые сервисы и контроллеры и исключить все остальное одновременно:

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

Entities. Эта часть более сложная, потому что @EntityScan не поддерживает эти фильтры. Хотя сущности не ссылаются на бины Spring, я предпочитаю загружать только сущности, необходимые для моего теста. Мне не удалось найти аннотации для сущностей, поддерживающих фильтрацию, но мы можем отфильтровать их программно, используя 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());
                }
            }

        }

    }


}
0 голосов
/ 17 февраля 2020

ComponentScan может работать с ломтиками. - Это на самом деле настроено на SpringBootApplication самой аннотации. Часть, которая заставляет тестовые срезы работать с ComponentScan: TypeExcludeFilter:

@ComponentScan(
    basePackages = "com.mycompany.someotherpackage",
    excludeFilters = {
      @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    })
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...