Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mockito Unit tests - Timestamps are different

Having some issues with a Mockito test.

I currently get this error:

Argument(s) are different! Wanted:
repository.save(
    uk.co.withersoft.docservice.repositories.hibernate.MetaDataEntity@3e437e6c
);
-> at uk.co.withersoft.docservice.datastore.impl.MetaDataStoreImplTest.storeClaimMetadata(MetaDataStoreImplTest.java:55)
Actual invocation has different arguments:
repository.save(
    uk.co.withersoft.docservice.repositories.hibernate.MetaDataEntity@3e361ee2
);

I'm pretty sure its because the times within MetaDataEntity are different

//This is what I should be getting

id = null
metaData = "{"caseReference":"CN00000001","claimReference":"LN00000001","rpsDocumentType":"REJ","documentTitle":"Claims LN00000001 (Claimant: Mr LOCAL HOST) REJ-Rejection letter"}"
batchId = 0
state = "Saved MetaData to DB"
lastUpdatedDate = {Timestamp@1517} "2018-07-25 18:39:21.993"
createdDate = {Timestamp@1518} "2018-07-25 18:39:21.993"

// This is actually what I get.

id = null
metaData = "{"caseReference":"CN00000001","claimReference":"LN00000001","rpsDocumentType":"REJ","documentTitle":"Claims LN00000001 (Claimant: Mr LOCAL HOST) REJ-Rejection letter"}"
batchId = 0
state = "Saved MetaData to DB"
lastUpdatedDate = {Timestamp@1530} "2018-07-25 18:39:49.274"
createdDate = {Timestamp@1531} "2018-07-25 18:39:52.716"

Here is my test case:

@Test
    public void storeClaimMetadata () throws JsonProcessingException {

        ClaimMetaData metaData = constructMetaData();

        MetaDataEntity mockResponseMetaDataEntity = new MetaDataEntity();
        mockResponseMetaDataEntity.setId(1);

        when(repository.save(any(MetaDataEntity.class))).thenReturn(mockResponseMetaDataEntity);

        Integer result = testSubject.storeClaimMetadata(metaData);

        assertEquals(Integer.valueOf(1), result);

        final ObjectMapper mapper = new ObjectMapper();
        String jsonMetaData = mapper.writeValueAsString(metaData);

        MetaDataEntity expectedMetaDataEntity = new MetaDataEntity(null,
                                                                   jsonMetaData,
                                                                   0,
                                                                   "Saved MetaData to DB",
                                                                   new Timestamp(System.currentTimeMillis()),
                                                                   new Timestamp(System.currentTimeMillis()));

        Mockito.verify(repository, times(1)).save(expectedMetaDataEntity);
    }

    //Creates a ClaimRequest
    private ClaimMetaData constructMetaData() {
        final ClaimMetaData metaData  = new ClaimMetaData("CN00000001",
                                                          "LN00000001",
                                                          "REJ",
                                                          "Claims LN00000001 (Claimant: Mr LOCAL HOST) REJ-Rejection letter");

        return metaData;
    }

Any help would be much appreciated. This has been driving me crazy!!

like image 396
Tom Withers Avatar asked Jan 28 '23 17:01

Tom Withers


2 Answers

This is exactly why people use dependency injection, so they can specify test collaborators that give back predictable results. Replace the hardcoded new Timestamp(System.currentTimeMillis) stuff with calls to Timestamp.from(Instant.now(clock)).

java.time.Clock is an interface that you can use to get your timestamp values. The real implementation can be injected into the code being tested, using one of the factory methods that returns a system clock, like this (using Spring Java configuration):

@Bean
public Clock clock() {
    return Clock.systemDefaultZone();
}

and for the test code you can have an implementation where you specify the time you want the clock to return:

@Before 
public void setUp() {
    clock = Clock.fixed(date.toInstant(), ZoneId.of("America/NewYork"));
    systemUnderTest.setClock(clock);
}
like image 57
Nathan Hughes Avatar answered Jan 31 '23 19:01

Nathan Hughes


This is "works as designed".

You are invoking a service that computes timestamps. Like, now.

Then you have a test case that has some setup going on, and fetches time stamps, too. Now.

Guess what: albeit these two "nows above are close to each other, there is still a bit of delay between them.

You are checking for equality, can only work when the time stamps are identical! But they aren't, because they are created one after the other, with very well noticeable delays in between!

Meaning: you need to look how you could control which timestamps are created within your application, like saying "the timestamps should be t1 and t2". So that your test can then check "I found t1 and t2".

Alternatively, you simply change your verification step: instead of trying to have "equal" objects (that can't be equal because different time stamps!), you could compare those parts that should be equal, and for the time stamps, you could check that they are "close enough".

like image 30
GhostCat Avatar answered Jan 31 '23 19:01

GhostCat