Home > Enterprise >  How to compare new Date in unit test?
How to compare new Date in unit test?

Time:05-20

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