I have this next class:
@Service
public class BusinessService {
@Autowired
private RedisService redisService;
private void count() {
String redisKey = "MyKey";
AtomicInteger counter = null;
if (!redisService.isExist(redisKey))
counter = new AtomicInteger(0);
else
counter = redisService.get(redisKey, AtomicInteger.class);
try {
counter.incrementAndGet();
redisService.set(redisKey, counter, false);
logger.info(String.format("Counter incremented by one. Current counter = %s", counter.get()));
} catch (JsonProcessingException e) {
logger.severe(String.format("Failed to increment counter."));
}
}
// Remaining code
}
and this this my RedisService.java class
@Service
public class RedisService {
private Logger logger = LoggerFactory.getLogger(RedisService.class);
@Autowired
private RedisConfig redisConfig;
@PostConstruct
public void postConstruct() {
try {
String redisURL = redisConfig.getUrl();
logger.info("Connecting to Redis at " + redisURL);
syncCommands = RedisClient.create(redisURL).connect().sync();
} catch (Exception e) {
logger.error("Exception connecting to Redis: " + e.getMessage(), e);
}
}
public boolean isExist(String redisKey) {
return syncCommands.exists(new String[] { redisKey }) == 1 ? true : false;
}
public <T extends Serializable> void set(String key, T object, boolean convertObjectToJson) throws JsonProcessingException {
if (convertObjectToJson)
syncCommands.set(key, writeValueAsString(object));
else
syncCommands.set(key, String.valueOf(object));
}
// Remaining code
}
and this is my test class
@Mock
private RedisService redisService;
@Spy
@InjectMocks
BusinessService businessService = new BusinessService();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void myTest() throws Exception {
for (int i = 0; i < 50; i++)
Whitebox.invokeMethod(businessService, "count");
// Remaining code
}
my problem is the counter always equals to one in logs when running tests
Counter incremented by one. Current counter = 1(printed 50 times)
and it should print:
Counter incremented by one. Current counter = 1
Counter incremented by one. Current counter = 2
...
...
Counter incremented by one. Current counter = 50
this means the Redis mock always passed as a new instance to BusinessService in each method call inside each loop, so how I can force this behavior to become only one instance used always for Redis inside the test method ??
Note: Above code is just a sample to explain my problem, but it's not a complete code.
Your conclusion that a new RedisService
is somehow created in each iteration is wrong.
The problem is that it is a mock object for which you haven’t set any behaviours, so it responds with default values for each method call (null for objects, false for bools, 0 for ints etc).
You need to use Mockito.when
to set behaviour on your mocks.
There is some additional complexity caused by the fact that:
set
method returns voidget
method.class BusinessServiceTest {
@Mock
private RedisService redisService;
@InjectMocks
BusinessService businessService = new BusinessService();
AtomicInteger atomicInt = null;
@BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void myTest() throws Exception {
// given
Mockito.when(redisService.isExist("MyKey"))
.thenReturn(false)
.thenReturn(true);
Mockito.doAnswer((Answer<Void>) invocation -> {
atomicInt = invocation.getArgument(1);
return null;
}).when(redisService).set(eq("MyKey"), any(AtomicInteger.class), eq(false));
Mockito.when(redisService.get("MyKey", AtomicInteger.class))
.thenAnswer(invocation -> atomicInt);
// when
for (int i = 0; i < 50; i++) {
Whitebox.invokeMethod(businessService, "count");
}
// Remaining code
}
}
Having said that, I still find your code questionable.