Ivan Karotki
Ivan Karotki

Reputation: 332

Mockito verify 'arguments are different', while they are identical

I am testing spring RestController. I use several prams to call controller. The call is successful and i got expected response. But Mockito is not able to verify mock call of service. Why?:

...
    private static final String TRACE_ID = "1";
    private static final String MAPPED_ID = "2";
...


    @Test
    void getLogMessages() throws Exception {
        LogMessageDto dto = easyRandom.nextObject(LogMessageDto.class);
        Integer slice = 0;
        Integer limit = 60;
        Integer serviceLimit = 50;
        Date startFrom = new Date();
        Date startTo = new Date();
        MultiValueMap<String, String > params = new LinkedMultiValueMap<>();
        params.put("traceId", Collections.singletonList(TRACE_ID));
        params.put("mappedId", Collections.singletonList(MAPPED_ID));
        params.put("slice", Collections.singletonList(slice.toString()));
        params.put("limit", Collections.singletonList(limit.toString()));
        params.put("startFrom", Collections.singletonList(startFrom.toString()));
        params.put("startTo", Collections.singletonList(startTo.toString()));
        Mockito.when(logMessageService.getLogMessages(anyString(), anyString(), anyInt(), anyInt(), any(Date.class),
                        any(Date.class)))
                .thenReturn(new PageImpl<>(Collections.singletonList(dto)));
        mockMvc.perform(get("/feed-logs-service/messages")
                        .queryParams(params))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.content[0].content", equalTo(asParsedJson(dto.getContent()))))
                .andExpect(jsonPath("$.content[0].fields", equalTo(asParsedJson(dto.getFields()))));
        verify(logMessageService).getLogMessages(eq(TRACE_ID), eq(MAPPED_ID), eq(slice), eq(serviceLimit), eq(startFrom),
                eq(startTo));
        verifyNoMoreInteractions(logMessageService);
    }

the test execution results in:

Argument(s) are different! Wanted:
service.LogMessageService#0 bean.getLogMessages(
    "1",
    "2",
    0,
    50,
    Sun Feb 06 14:52:13 MSK 2022,
    Sun Feb 06 14:52:13 MSK 2022
);
-> at controller.LogMessageControllerTest.getLogMessages(LogMessageControllerTest.java:97)
Actual invocations have different arguments:
service.LogMessageService#0 bean.getLogMessages(
    "1",
    "2",
    0,
    50,
    Sun Feb 06 14:52:13 MSK 2022,
    Sun Feb 06 14:52:13 MSK 2022
);

intellij idea shows additional empty line when i click 'Click to see difference' link, is it the reason?, and how to pass trough? Thanks for any suggestions.

EDIT: controller method:

    @GetMapping("/messages")
    public Page<LogMessageDto> getLogMessages(@RequestParam String traceId, @RequestParam String mappedId,
            @RequestParam(required = false) Integer slice,
            @RequestParam(required = false, defaultValue = SIZE_LIMIT) Integer limit,
            @RequestParam(required = false) @DateTimeFormat(pattern = "EEE MMM dd HH:mm:ss ZZZ yyyy") Date startFrom,
            @RequestParam(required = false) @DateTimeFormat(pattern = "EEE MMM dd HH:mm:ss ZZZ yyyy") Date startTo) {
        limit = checkLimit(limit);
        return logMessageService.getLogMessages(traceId, mappedId, slice, limit, startFrom, startTo);
    }

Upvotes: 1

Views: 1363

Answers (1)

Polygorial
Polygorial

Reputation: 221

From the test:

Date startFrom = new Date();

These values are parsed to a String:

params.put("startFrom", Collections.singletonList(startFrom.toString()));

But when verifying it's not the String representation that's validated:

verify(logMessageService).getLogMessages(..., eq(startFrom), ...);

I could tell you the problem with this. But as an exercise to learn how Date works I suggest you compare the startFrom.getTime() from the variable created in the test, and the call to logMessageService.getLogMessages(...)

Edit to add an explanation of the problem

The Date is represented as a 64 bit integer internally, down to millisecond. That's what's used when the equals-method is called. When the toString method is called it's converted to a human readable format, and by default loses the millisecond bit. When it's converted back to a Date in the controller it's with zeros as the milliseconds. Which is verified against the Date with milliseconds.

Upvotes: 1

Related Questions