How to test spring configuration classes?

时间:2016-01-20 19:57:44

标签: java spring spring-mvc junit

I have a spring application whith configuration classes where instance the beans.

Aplication class:

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

    //....
}

MvcConfiguration class:

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

    //....

}

And SecurityConfiguration class:

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

}

How I can test them with JUnit? How to test the beans are created in the spring context?

5 个答案:

答案 0 :(得分:20)

总之 - “不要”,这就是疯狂。

您真正想要的是更高级别的测试,它们使用您的Spring配置但仍然专注于行为而不是实现

例如,查看您的安全配置 - 您并不真正关心调用configure方法或它的作用,您要测试的是:

  1. 静态页面不需要身份验证
  2. 其他页面需要身份验证
  3. 登录作品
  4. 退出作品
  5. 使用Spring进行DI和安全只是 这些事情是如何实现的,而你的测试应该集中在这些事情实际工作的事实上。

答案 1 :(得分:9)

您应该能够使用 @ContextConfiguration 注释测试配置。例如,可以像这样测试SecurityConfiguration类:

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

答案 2 :(得分:7)

我相信这只能通过集成测试来实现。

单元测试的目的不是检查整个Spring Context是否成功创建。

您可以使用模拟等测试单元测试的每个配置方法来检查它们是否正常,但整个Spring Context事物是一个集成测试。

我通过执行Spring Docs所谓的“Spring Unit Test”(对我来说更像是控制器+视图的集成测试)来进行配置测试

我们的想法是,如果您可以为Controller集成测试运行Spring Context,那么您的配置就可以了。

关于如何进行这种测试的春季文档有一整章。 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html

答案 3 :(得分:1)

如果可以在测试环境中实例化所有bean,则可以在JUnit测试中构建上下文。您可以使用AnnotationConfigApplicationContext及其scan()方法执行此操作。

这样的测试应该足以快速验证配置。你可以从那里开始,从上下文中获取bean以进行更复杂的测试。

一些陷阱:

  • 对于延迟初始化的bean,可能效果不佳
    • 您可能需要使用getBean()从上下文中为这样的类实际请求实例以确保它已创建 - 您可以通过这种方式测试该期望
  • 可能并非总是可能,例如如果您将某个数据库连接作为依赖项,并且在测试环境中无权访问该数据库
    • 您可能需要为测试目的模拟这些bean

答案 4 :(得分:1)

对于基本的bean测试和一些高级属性配置,我使用ApplicationContextRunner()测试这些类。

https://dev.to/stack-labs/how-to-test-configuration-class-in-spring-boot-16ai

对于Spring Security @Configuration类,我将加载该类,然后定义一个控制器以查看其上的安全性是否正确定义。