rayman
rayman

Reputation: 21616

How to control the @PostConstruct when integration testing using Spring

I have spring bean class RedisRepo inside I am initialising my database connection with @PostConstruct:

 @PostConstruct
    public void init() {
        logger.debug("RedisRepo, Init.");
        client = new RedisClient(REDIS_HOST, REDIS_PORT);
        ...
    }

I am creating this bean using java config at SpringConfiguration.class:

 @Bean
    @Scope("singleton")
    public RedisRepo redisjRepo() {
        return new RedisRepo();
    }

I started to build Integration tests using Spring. I am using the same configuration class (SpringConfiguration.class) for tests:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SpringConfiguration.class)

My Test class using embedded-redis so I need to init it right before I start my tests:

 @Before
    public void init() throws IOException {
//init embedded-redis
}

The problem is when I start the tests the @PostConstruct of the RedisRepo class executed before my integration-test init() class (past below) which leading me to null since my embedded redis hasnt initialised just yet.

How could I avoid it? Mybe I am not doing something right?

Thanks, ray.

Upvotes: 1

Views: 3124

Answers (1)

luboskrnac
luboskrnac

Reputation: 24571

I would suggest to consider using spring Boot auto-configuration (@EnableAutoConfiguration or @SpringBootApplication) to initialize Redis connection. You can use these Spring Boot properties to customize Redis:

# REDIS (RedisProperties)
spring.redis.database= # database name
spring.redis.host=localhost # server host
spring.redis.password= # server password
spring.redis.port=6379 # connection port
spring.redis.pool.max-idle=8 # pool settings ...
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.sentinel.master= # name of Redis server
spring.redis.sentinel.nodes= # comma-separated list of host:port pairs

This would remove need for connection initialization in @PostConstruct production code and you can just autowire your Redis related beans from context dusting testing.

EDIT1:

To populate Redis instance before testing method you can use @BeforeMethod (using TestNg) or @Before (using JUnit). To populate it before the test but after the context is initialized use @PostConstruct in test class.

EDIT2:

You asked on generic rule how to overcome need for initialization of resources in @PostConstruct. I believe your problem is how you are wiring beans in your application.

Your @PostConstruct initialization is done in other bean, where RedisClient is stored as variable client. I would argue that it most probably mixing of concerns. If you register RedisClient bean into spring context this way:

@Bean
public RedisClient redisClient() {
    RedisClient client = new RedisClient(REDIS_HOST, REDIS_PORT);
    ...
    return client;
}

You can just autowire it into bean where you had @PostConstruct initialization. You are also able to autowire it during test. If RedisClient is not thread-safe, you may want to consider prototype or request scope for it.

With this approach, I extremely rarely use @PostConstruct and use Spring IoC container to handle all the reusable resource instances.

Upvotes: 1

Related Questions