I have service
with method :
Entity entity = new Entity(new Date(), 1, 2L);
return entityRepository.save(entity);
And my test :
@Test
public void testSaveEntity() {
HistoryEntity entity = new HistoryEntity(new Date(), 1, 2L);
entityService.saveEntityForCandidate(1, 2L);
verify(entityRepository, times(1)).save(entity);
}
If Entity
equals()
not compared Date
then everything is all right but if compare Date
then
test throws out Argument(s) are different!
CodePudding user response:
You have 2 options:
Option 1: Use Clock class to control time
Instead of using new Date()
:
- inject a
Clock
instance to your service - use its methods to retrieve current time
- In your test code, use
Clock.fixed
to control current time
See Guide to the Java Clock Class
Option 1: Relax your matching requirements
Use Mockito ArgumentMatchers to relax mtching requirements - use any(YourEntity.class)
to match any YourEntity
, or write a custom argument matcher for your Entity.
CodePudding user response:
You may be having an issue with the definition of your equals
method.
You should define under which circumstances two different Entities are considered equal:
Are two Entities equal only if their date value is within the same
millisecond or do we only care about seconds, minutes or days?
Similar to handling floating point values, this acceptable difference can be calculated similar to Calculating the difference between two Java date instances
CodePudding user response:
The problem is, when you create two Date
in different moment, they will not equal to each other. So your test will fail. This is a simple proof of concept:
@Test
public void test_date_equal() throws Exception {
Date date1 = new Date();
Thread.sleep(1);
Date date2 = new Date();
Assert.assertEquals(date1, date2); // fail
}
As a result, comparing two new Date
is unstable in most case. I think you should avoid directly using new Date
in different place as possible.
I have an approach but you should slightly refactor your production code. The idea is just adding a date parameter on your saveEntityForCandidate
:
@Service
public class EntityService {
public boolean saveEntityForCandidate(Date date, int whatever, long whatever2) {
// ...
Entity entity = new Entity(date, whatever, whatever2);
return entityRepository.save(entity);
}
}
in test:
@Test
public void testSaveEntity() {
Date date = new Date();
HistoryEntity entity = new HistoryEntity(date, 1, 2L);
entityService.saveEntityForCandidate(date, 1, 2L);
verify(entityRepository, times(1)).save(entity);
}
It will work as you expect because no arguments are different between your two entities.
If you cannot refactor your code, then you can try Powermock. But mocking an object which does not belong to you is considered as an anti-pattern in test.
https://stackoverflow.com/a/30415404/5485454
CodePudding user response:
Instead of relying on the equals
method used in your solution:
verify(entityRepository, times(1)).save(entity);
you could try to capture the Argument and assert it in the next step as described in Verify object attribute value with mockito
ArgumentCaptor<Entity> argument = ArgumentCaptor.forClass(Entity.class);
verify(entityRepository, times(1)).save((argument.capture());
assertEquals(1, argument.getValue().getWhatever());