5

I have service with method :

Entity entity = new Entity(new Date(), 1, 2L);
return entityRepository.save(entity);

And my test :

@Test
public void testSaveEntity() {
    Entity entity = new Entity(new Date(), 1, 2L);
    entityService.saveEntity(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!

Uga Rimad
  • 97
  • 6
  • 2
    If I may ask, why are you using `Date`? – MC Emperor May 19 '22 at 13:03
  • 1
    I strongly recommend you don’t use `Date`. That class is poorly designed and long outdated. Instead use an appropriate class from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. May 19 '22 at 13:32
  • 1
    Despite the name a `Date` does not represent a date but a point in time with millisecond precision. So if you called `new Date()` with a few milliseconds in between, you were most likely getting two objects that were not equal. Confusing, yes. Use java.time and as Lesiak says, use `java.time.Clock`. – Ole V.V. May 19 '22 at 13:37
  • 1
    Java has *two* `Date` classes, `java.util.Date` and `java.sql.Date`. Both are terribly flawed, and should be avoided. Both were years ago supplanted by the modern *java.time* classes. Specifically replaced by `Instant` and `LocalDate`, respectively. – Basil Bourque May 19 '22 at 18:52

4 Answers4

3

As I understand it is impossible to change the service and the entity. Then in such cases it is better to use ArgumentMatcher<> in Mockito.

Step 1 :

@AllArgsConstructor
public class EntityMatcher implements ArgumentMatcher<Entity> {

    private Entity left;

    @Override
    public boolean matches(Entity right) {
        return (left.getEventDate().getTime() - right.getEventDate().getTime() <= 1000));
    }
}

Here you ovverride equals which will be compare objects. mathces can be ovveride as you like. I think the difference in one second is enough.

Step 2:

verify(entityRepository).save(argThat(new EntityMatcher(new Entity(new Date(), 1, 2L))));

Other case : Most likely, in other tests, such a situation may arise that this entity will also need to be checked when

when(entityRepository.save(any(Entity.class))).thenReturn(new Entity(new Date(), 1, 2L));
Jeka_FRI
  • 71
  • 1
  • 9
2

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.

Lesiak
  • 22,088
  • 2
  • 41
  • 65
  • Unfortanetly, `Clock` I can't change it. `any(Date.class)` seems to be used in the `when()` construction – Uga Rimad May 19 '22 at 09:01
  • 1. I'm sorry but I don't understand. Do you mean you cannot change your code? 2. You can use argument matchers in `verify` as well. – Lesiak May 19 '22 at 10:33
2

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

0

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());