web-dev-qa-db-fra.com

Comment tester les classes de configuration du ressort?

J'ai une application Spring avec des classes de configuration où les beans sont par exemple.

Classe d'application:

@Configuration
@EnableAspectJAutoProxy
@EnableSpringDataWebSupport
@EnableTransactionManagement
@ComponentScan(basePackageClasses = Application.class)
@PropertySource(value = {"classpath:foo.properties"})
@EnableJpaRepositories(basePackageClasses = Application.class)
@EnableJpaAuditing
public class Application {

    @Inject
    private Environment env;

    @Bean
    JndiTemplate jndiTemplate() {
        return new JndiTemplate();
    }

    @Bean
    public DataSource dataSource() {        
        DataSource dataSource = getDataSource();
        if (dataSource == null) {
            dataSource = new BasicDataSource();
            ((BasicDataSource) dataSource).setUsername(env.getProperty("jdbc.user"));
            ((BasicDataSource) dataSource).setPassword(env.getProperty("jdbc.password""));

            ((BasicDataSource) dataSource).setDriverClassName(env.getProperty("jdbc.driverClassName"));
            ((BasicDataSource) dataSource).setUrl(env.getProperty("jdbc.url"));
        }
        return dataSource;
    }

    @Bean
    public PlatformTransactionManager transactionManager() {
        EntityManagerFactory factory = entityManagerFactory().getObject();
        return new JpaTransactionManager(factory);
    }

    //....
}

Classe MvcConfiguration:

@Configuration
@ComponentScan(basePackageClasses = Application.class, includeFilters = @Filter({Controller.class, Component.class}), useDefaultFilters = true)
class MvcConfiguration extends WebMvcConfigurationSupport {
    private static final String MESSAGES = "classpath:/i18n";

    private static final String VIEW_PREFIX = "/WEB-INF/views/";

    @Inject
    private Environment env;

    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        RequestMappingHandlerMapping requestMappingHandlerMapping = super.requestMappingHandlerMapping();
        requestMappingHandlerMapping.setUseSuffixPatternMatch(false);
        requestMappingHandlerMapping.setUseTrailingSlashMatch(true);

        return requestMappingHandlerMapping;
    }

    @Bean(name = "messageSource")
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename(MESSAGES);
        messageSource.setCacheSeconds(5);

        return messageSource;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/").addResourceLocations("/static/**");
    }

    @Bean
    public MultipartResolver filterMultipartResolver(){
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setMaxUploadSize(Long.parseLong(env.getProperty("multipart.max.size")));
        return resolver;
    }

    //....

}

Et classe SecurityConfiguration:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    //....

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Logout por POST con el valor de token csrf
        http.authorizeRequests()
                .antMatchers("/static/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .failureUrl("/login?error=1")
                .loginProcessingUrl("/authenticate")
                .and()
            .logout()
                .logoutUrl("/logout")
                .logoutSuccessUrl("/signin")
                .permitAll();
    }

}

Comment puis-je les tester avec JUnit? Comment tester les haricots créés dans le contexte printanier?

12
oscar

Je crois que cela ne sera réalisé qu'avec un test d'intégration.

Le but des tests unitaires n'est pas de vérifier si l'ensemble du contexte Spring est créé avec succès.

Vous pouvez tester chaque méthode de configuration avec un test unitaire en utilisant des simulations et etc pour vérifier si elles sont correctes, mais tout le truc du contexte Spring est un test d'intégration.

J'utilise pour faire ce test de configuration en faisant ce que Spring Docs appelle "Spring Unit Test" (qui pour moi ressemble plus à un test d'intégration des contrôleurs + vues)

L'idée est que, si vous pouvez exécuter un contexte Spring pour un test d'intégration de contrôleur, vos configurations sont OK.

Il y a tout un chapitre sur les documents du printemps sur la façon de faire ce genre de test. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html

9
renanleandrof

En un mot - "ne faites pas", de cette façon jette la folie.

Ce que vous voulez vraiment, ce sont des tests de niveau supérieur qui utilisent votre configuration Spring mais qui sont toujours axés sur comportement pas implémentation.

Par exemple, en regardant votre configuration de sécurité - peu vous importe que la méthode configure soit appelée, ou ce qu'elle fait, ce que vous voulez tester est:

  1. Les pages statiques ne nécessitent pas d'authentification
  2. D'autres pages nécessitent une authentification
  3. Connexion aux travaux
  4. Déconnexion des travaux

Utiliser Spring pour DI et la sécurité est simplement comment ces choses sont implémentées alors que vos tests doivent être axés sur le fait que ces choses fonctionnent réellement.

23
tddmonkey

Vous devriez pouvoir tester la configuration à l'aide de l'annotation @ ContextConfiguration. Par exemple, la classe SecurityConfiguration peut être testée comme ceci:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SecurityConfiguration.class) 
class SecurityConfigurationTest {

    @Autowired
    SecurityConfiguration securityConfiguration;

    @Test
    public void passwordEncoderTest() throws Exception {
        final BCryptPasswordEncoder encoder = securityConfiguration.passwordEncoder();
        final String encodedPassword = encoder.encode("password");
        assertNotNull(encodedPassword);
    }
}
9
kyleus

Vous pouvez créer le contexte dans un test JUnit, à condition que tous les beans puissent être instanciés dans l'environnement de test. Vous pouvez utiliser AnnotationConfigApplicationContext et sa méthode scan() pour ce faire.

Un tel test devrait être suffisant pour une vérification rapide de la configuration. Et vous pouvez partir de là, obtenir des beans à partir du contexte pour un test plus complexe.

Quelques pièges:

  • Pourrait ne pas fonctionner correctement pour les beans initialisés paresseux
    • Vous devrez peut-être réellement demander une instance à partir du contexte en utilisant getBean() pour une telle classe pour vous assurer qu'elle est créée - vous pouvez tester cette attente de cette façon
  • Peut ne pas être toujours possible, par ex. si vous avez une connexion à la base de données en tant que dépendance et n'avez pas accès à la base de données dans l'environnement de test
    • Vous devrez peut-être vous moquer de ces beans pour les besoins du test
1
Jiri Tousek