1

I have a Java Spring application with a RedisService for interacting with a Redis database. The RedisService constructor takes a JedisPooled object as the constructor dependency for using the Redis connection:

@Repository public class RedisService { private final JedisPooled jedisPooled; @Autowired public RedisService(final JedisPooled jedisPooled) { this.jedisPooled = jedisPooled; } public String set(final String key, final String val) { return jedisPooled.set(key, val); } public String get(final String key) { return jedisPooled.get(key); } } 

I use a RedisServiceConfig class to manage the configuration for this service:

@Configuration public class RedisServiceConfig { @Bean public RedisService redisService(@Value("${redis.auth.password}") final String redisAuthPassword, @Value("${redis.endpoint}") final String redisEndpoint, @Value("${redis.port}") final int redisPort) { final JedisPooled jedisPooled; try { jedisPooled = new JedisPooled(redisEndpoint, redisPort, redisAuthPassword); } catch (final Exception e) { throw new JedisConnectionException("Error connecting to Redis"); } return new RedisService(jedisPooled); } } 

How can I unit test the exception handling in the config class? I've tried something like this, but I can't quite get it to work:

public class RedisServiceConfigTest { @Mock private JedisPooled mockJedisPooled; @InjectMocks private RedisServiceConfig redisServiceConfig; @Test public void testRedisServiceBeanCreation() { when(mockJedisPooled.created()).thenThrow(new JedisConnectionException("Connection error")); // I need something ^ like this? final RedisService redisService = redisServiceConfig.redisService("testPassword", "localhost", 6379); Assertions.assertNotNull(redisService); } } 

I've tried mocking the mockJedisPooled and tried to force it to throw exceptions. Is this the correct approach for unit testing a config class like this?

2
  • 2
    For that constructor, I don't see anything in the constructor chain that would throw, so the try/catch should be removed. Commented Apr 21 at 12:36
  • Good point. Because the constructor doesn't throw, I have removed the try/catch, thank you Commented May 9 at 10:44

1 Answer 1

2

It looks like some refactoring is needed here.

You're trying to unit test a configuration class that creates a JedisPooled instance inside a @Bean method. Since JedisPooled is instantiated directly in the method, it can't be mocked via standard @Mock or @InjectMocks.

Try to split the creation of JedisPooled into a separate @Bean, like this:

@Configuration public class RedisServiceConfig { @Bean public JedisPooled jedisPooled( @Value("${redis.auth.password}") String password, @Value("${redis.endpoint}") String endpoint, @Value("${redis.port}") int port) { try { return new JedisPooled(endpoint, port, password); } catch (Exception e) { throw new JedisConnectionException("Error connecting to Redis", e); } } @Bean public RedisService redisService(JedisPooled jedisPooled) { return new RedisService(jedisPooled); } } 

Let's simulate a connection error in the JedisPooled constructor, to make the test meaningful and realistic:

public class JedisPooled { private String password; private String endpoint; private int port; public JedisPooled(String password, int port, String endpoint) { this.password = password; this.endpoint = endpoint; this.port = port; // Simulate a connection failure for testing purposes if ("invalid-host".equals(endpoint)) { throw new JedisConnectionException("Error connecting to Redis"); } } ... 

Now you can mock JedisPooled in your unit test, and easily test the logic, something like:

@ExtendWith(MockitoExtension.class) class RedisServiceConfigTest { @Mock private JedisPooled mockJedisPooled; private RedisServiceConfig redisServiceConfig; @BeforeEach void setUp() { redisServiceConfig = new RedisServiceConfig(); } @Test void testRedisServiceBeanUsesInjectedJedisPooled() { RedisService redisService = redisServiceConfig.redisService( mockJedisPooled ); assertNotNull(redisService); assertEquals( "mockJedisPooled", redisService.getJedisPooled().toString() ); } @Test void testJedisPooledBeanThrowsCustomExceptionWhenFails() { JedisConnectionException thrown = assertThrows( JedisConnectionException.class, () -> { config.jedisPooled("test-pass", "invalid-host", 9999); } ); assertEquals("Error connecting to Redis", thrown.getMessage()); } } 

If you want to play with when(...).then Throw(...), then you can do this:

when(mockJedisPooled.get("someKey")) .thenThrow(new JedisConnectionException("boom")); 

But this is for testing the RedisService itself, not the configuration.

If refactoring is not possible, you can use PowerMock, but this is not recommended in the general case (more complicated and depends on the bytecode hacks).

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for the detailed answer. I have removed the try/catch as the constructor doesn't actually throw anything.
You're welcome!) Cool to hear it was useful.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.