在 junit 测试中禁用 RedisHttpSessionConfiguration

Posted

技术标签:

【中文标题】在 junit 测试中禁用 RedisHttpSessionConfiguration【英文标题】:Disabling RedisHttpSessionConfiguration in junit tests 【发布时间】:2016-04-17 03:42:46 【问题描述】:

我正在使用spring-bootspring-session 构建一个简单的rest api。生产应用应该使用 redis-session 实现,而测试应该使用默认的(非 redis)spring-session。

但是,当在项目中声明 spring-boot-starter-redis maven 依赖项时,spring-boot 将自动创建一个 org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration,而 @EnableRedisHttpSession 注释不会出现在测试配置中的任何位置。

我的设置

应用代码:

// base session config for both prod/dev and test/ci
public class BaseSessionConfig 

  @Bean
  public HeaderHttpSessionStrategy sessionStrategy() 
    return new HeaderHttpSessionStrategy();
  



// enable the redis session only in these profiles
@Configuration
@EnableRedisHttpSession
@Profile("prod", "dev", "local", "vagrant")
public class RedisSessionConfig extends BaseSessionConfig 



测试:

@WebIntegrationTest
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyApplication.class, MyTestApplicationConfig.class)
public class AuthenticationResourceTest 

  private RestTemplate restTemplate = new TestRestTemplate();

  @Test
  public void testUnauthenticated() 

    HttpHeaders headers = restTemplate.getForEntity("http://localhost:8080/api/session", String.class).getHeaders();

    // ...    
  



// session configuration for the test environments
// no redis enabled    
@Configuration
@Profile("ci", "test")
public class MyTestApplicationConfig extends BaseSessionConfig 



如何在 junit 测试中禁用 redis 会话自动配置?

测试期间的堆栈跟踪:

java.lang.IllegalStateException: Failed to load ApplicationContext

    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
    at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:228)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:230)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:249)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'enableRedisKeyspaceNotificationsInitializer' defined in class path resource [org/springframework/session/data/redis/config/annotation/web/http/RedisHttpSessionConfiguration.class]: Invocation of init method failed; nested exception is org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1578)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:764)
    at org.springframework.boot.SpringApplication.doRun(SpringApplication.java:357)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
    at org.springframework.boot.test.SpringApplicationContextLoader.loadContext(SpringApplicationContextLoader.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
    ... 24 more
Caused by: org.springframework.data.redis.RedisConnectionFailureException: Cannot get Jedis connection; nested exception is redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:162)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:251)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.getConnection(JedisConnectionFactory.java:58)
    at org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration$EnableRedisKeyspaceNotificationsInitializer.afterPropertiesSet(RedisHttpSessionConfiguration.java:166)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
    ... 40 more
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
    at redis.clients.util.Pool.getResource(Pool.java:50)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:99)
    at redis.clients.jedis.JedisPool.getResource(JedisPool.java:12)
    at org.springframework.data.redis.connection.jedis.JedisConnectionFactory.fetchJedisConnector(JedisConnectionFactory.java:155)
    ... 45 more
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused
    at redis.clients.jedis.Connection.connect(Connection.java:164)
    at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:82)
    at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1641)
    at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:85)
    at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
    at redis.clients.util.Pool.getResource(Pool.java:48)
    ... 48 more
Caused by: java.net.ConnectException: Connection refused
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at redis.clients.jedis.Connection.connect(Connection.java:158)
    ... 55 more

编辑

我想通了。尽管配置上没有 EnableRedisSession 注释,但 RedisHttpSessionConfig 在测试中加载的原因是 spring-boot-starter-web 附带的自动配置:SessionAutoConfiguration

所以在测试中,这是需要从自动配置中排除的类:

@WebIntegrationTest
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = TestAppConfig.class)
public class AuthenticationResourceTest 

  private RestTemplate restTemplate = new TestRestTemplate();

  @Test
  public void testUnauthenticated() 

    HttpHeaders headers = restTemplate.getForEntity("http://localhost:8080/api/session", String.class).getHeaders();

  




@Configuration
@SpringBootApplication(exclude = SessionAutoConfiguration.class, SessionRedisHttpConfiguration.class)
public class TestAppConfig 

  @Configuration
  static class SessionConfig extends BaseSessionConfig 

  


【问题讨论】:

尝试使用 @SpringBootApplication 注释 MyTestApplicationConfig 并执行此操作 @SpringBootApplication(exclude = RedisHttpSessionConfiguration.class) 【参考方案1】:

使用@ActiveProfiles testing 注释启用某些配置文件进行测试。如果您不激活在 RedisSessionConfig 上定义的配置文件,则不应加载该配置。

但还要确保 RedisHttpSessionConfiguration 不是通过自动配置加载的。

评论反应:

我不确定 Redis 是否有它的自动配置类。 Looking into Spring Boot,它有。所以你需要在测试时排除它。 Look into this SO thread how to do it.

【讨论】:

不幸的是,这不起作用。我已经尝试过类似的建议,就像这里提到的那样:***.com/questions/26698071/… - 但RedisHttpSessionConfiguration 仍然加载到测试中(也启用了@ActiveProfiles("test")。据我了解spring-boot autoconfig,需要有一个@ 987654331@ 包含自动配置类的文件 + 配置上的一些 @Conditional 注释。但是,此配置没有:git.io/vzO1C

以上是关于在 junit 测试中禁用 RedisHttpSessionConfiguration的主要内容,如果未能解决你的问题,请参考以下文章

在不创建上下文的情况下禁用 Spring 和 Junit5 测试

SpringBoot 在运行 Junit 测试时禁用 DataSourceAutoconfigure 错误

Junit实现执行条件,以编程方式禁用测试功能

JUnit5 快速指南

为啥不能在 junit 配置中添加程序参数?

[JUnit] JUnit5 基础 4 - Maven 集成,参数化测试,重复测试 [完]