Problem:

Let’s suppose you have to implement a publisher with Redis and Spring Boot and you have to bringing down the instance when redis is unavailable.

The code could be something like that:

import org.springframework.data.redis.RedisConnectionFailureException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class MessagePublisherImpl implements MessagePublisher {

    private final StringRedisTemplate redisTemplate;

    public MessagePublisherImpl(final StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void publish(final String message) {
    	try {
    		redisTemplate.convertAndSend("myTopic", message);
    	} catch (RedisConnectionFailureException e) {
    		log.error("Unable to connect to redis. Bringing down the instance");
    		System.exit(1);
    	}
    }

}

This implementation is fine and It works as we expected but now, we have a problem…We want to test the code, specifically, we want to test booth branches in the publish method.

For that mision, first of all, I created two tests:

  1. Given a message When is published Then is published by redis
	@Test
	@DisplayName("Given a message When is published Then is published by redis")
	public void givenAMessageWhenIsPublishedThenIsPublishedByRedis() {
		// given
		final String message = "Hi!";

		// when
		messagePublisher.publish(message);

		// then
		verify(redisTemplate, times(1)).convertAndSend(anyString(), eq(message));
	} 
  1. Given the Redis instance down When a message is published Then the container is bringing down
	@Test
	@DisplayName("Given the Redis instance down When a message is published Then the container is bringing down")
	public void givenTheRedisInstanceDownWhenAMessageIsPublishedThenTheContainerIsBringingDown() {
		// given
		willThrow(new RedisConnectionFailureException("")).given(redisTemplate)
				.convertAndSend(anyString(), any());
		final String message = "Hi!";

		// when
		messagePublisher.publish(message);

		// then
		// ???
	}

That’s right, It’s seems to be fine, now, when we executed the tests something weird occurrs.

Unit tests are not ending

Our tests execution are not ending due to the System.exit introduced in the publisher class.

How could I fix this issue?

Solution:

The most obvious answer is trying to mocking System.exit, so, let’s go.

As the javadoc shows us, System.exit method calls the exit method in class Runtime, thus, we should put the focus on that method.

We could create a Runtime Spy and exchange it with the static variable of the Runtine class and when the second test ends, we could leave everything as it was before.

Our test will be as following:

	@Test
	@DisplayName("Given the Redis instance down When a message is published Then the container is bringing down")
	public void givenTheRedisInstanceDownWhenAMessageIsPublishedThenTheContainerIsBringingDown() throws Exception {
		// given
		willThrow(new RedisConnectionFailureException("")).given(redisTemplate).convertAndSend(anyString(), any());
		final String message = "Hi!";
		Runtime originalRuntime = Runtime.getRuntime();
		Runtime spyRuntime = spy(originalRuntime);
		doNothing().when(spyRuntime).exit(eq(1));
		setField(Runtime.class, "currentRuntime", spyRuntime);

		// when
		messagePublisher.publish(message);

		// then
		verify(spyRuntime, times(1)).exit(eq(1));
		setField(Runtime.class, "currentRuntime", originalRuntime);
	}

	private void setField(Class<?> clazz, String name, Object spy) throws Exception {
		final Field field = clazz.getDeclaredField(name);
		field.setAccessible(true);
		field.set(null, spy);
	}

With this improve, our test suite runs fine and we have added an assert to the tests something important and that it did not have before.

Unit tests are fine

Conclusions:

Thanks to Mockito and Java Reflection we can test almost all the cauisics that we find in our day to day.

The full source code for this article is available over on GitHub.