Mock a method which manipulates an input parameter

2,820 views
Skip to first unread message

Bill Comer

unread,
Nov 20, 2017, 3:45:53 AM11/20/17
to mockito

I am trying to mock a database Dao call that saves an object in to a DB. 


The Dao calls returns the new PK, but also modifies the passed object by updating the PK.


My test is working as I want except the verify fails saying I am not passing the expected object. yet when I pause in the debugger I can see that the insert is indeed accepting an object with no PK. It is then modifying it & the calling controller can see the changed object. 


I have tried running the code with the additional commented out lines (in an attempt to know that the verify() is using an unmodified object) replacing the verify() but that also fails 


Any thoughts please ?


@Test
public void insertSampleAnalyteLabConfig() throws Exception {
int newInt = 99;

SampleAnalyteLabConfig sampleAnalyteLabConfig = createMockSampleAnalyteLabConfig(null);
//SampleAnalyteLabConfig sampleAnalyteLabConfig2 = createMockSampleAnalyteLabConfig(null);

//assertEquals("at this point they should be the same", sampleAnalyteLabConfig, sampleAnalyteLabConfig2);

when(sampleAnalyteLabConfigService.save(sampleAnalyteLabConfig)).thenAnswer( invocation -> {
SampleAnalyteLabConfig foo = (SampleAnalyteLabConfig) invocation.getArguments()[0];
foo.setId(newInt);
return new Long(newInt);
}
);

mockMvc.perform(post("/api/lab/sampleanalytelab")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content(getBytes(sampleAnalyteLabConfig))).andExpect(status().isCreated());

//verify(sampleAnalyteLabConfigService, times(1)).save(sampleAnalyteLabConfig2);
verify(sampleAnalyteLabConfigService, times(1)).save(sampleAnalyteLabConfig);
}


With the code as above I get the following:



Argument(s) are different! Wanted:
sampleAnalyteLabConfigService.save(
    SampleAnalyteLabConfig{{id=0}sampleId=2, analyteId=3, labId=1, numberOfRounds=null, absolutePerformanceScoreWarning=null}
);
-> at com.lgc.controllers.LabControllerTest.insertSampleAnalyteLabConfig(LabControllerTest.java:240)
Actual invocation has different arguments:
sampleAnalyteLabConfigService.save(
    SampleAnalyteLabConfig{{id=99}sampleId=2, analyteId=3, labId=1, numberOfRounds=null, absolutePerformanceScoreWarning=null}
);
-> at com.lgc.controllers.LabController.saveSampleAnalyteLabConfig(LabController.java:165)

Comparison Failure:  <Click to see difference>

Argument(s) are different! Wanted:
sampleAnalyteLabConfigService.save(
    SampleAnalyteLabConfig{{id=0}sampleId=2, analyteId=3, labId=1, numberOfRounds=null, absolutePerformanceScoreWarning=null}
);
-> at com.lgc.controllers.LabControllerTest.insertSampleAnalyteLabConfig(LabControllerTest.java:240)
Actual invocation has different arguments:
sampleAnalyteLabConfigService.save(
    SampleAnalyteLabConfig{{id=99}sampleId=2, analyteId=3, labId=1, numberOfRounds=null, absolutePerformanceScoreWarning=null}
);
-> at com.foo.controllers.LabController.saveSampleAnalyteLabConfig(LabController.java:165)




Malte Finsterwalder

unread,
Nov 20, 2017, 4:26:00 AM11/20/17
to mockito
Hi Bill,

I'm not sure I can help. I don't have enough information. But some questions that come to my mind:
- What does getBytes(sampleAnalyteLabConfig) do? Is there some serialization involved, before the object is passed to the location, where save() is called in the code?
- Mockito probably saves the object that was passed to save() and stores it somewhere for later verify. But inside your Answer-code, you change this object. What does this do to the "to be verified"-Object?

Greetings,
   Malte

--
You received this message because you are subscribed to the Google Groups "mockito" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mockito+unsubscribe@googlegroups.com.
To post to this group, send email to moc...@googlegroups.com.
Visit this group at https://groups.google.com/group/mockito.
To view this discussion on the web visit https://groups.google.com/d/msgid/mockito/09982c57-e2a0-4fa6-bb38-613b9d7bc661%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Bill Comer

unread,
Nov 20, 2017, 10:33:15 AM11/20/17
to mockito
Malte,

Yes getBytes sderializes the object:

private byte[] getBytes(SampleAnalyteLabConfig sdDao) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(sdDao);
}
 

Jige Yu

unread,
Nov 20, 2017, 11:55:40 AM11/20/17
to mockito
I normally use @Spy to implement side-effects or to enforce invariants:
@Spy private MockSampleAnalyteService sampleAnalyteService;

@Test
public void insertSampleAnalyteLabConfig() throws Exception {
int newInt = 99;

SampleAnalyteLabConfig sampleAnalyteLabConfig = createMockSampleAnalyteLabConfig(null);
//SampleAnalyteLabConfig sampleAnalyteLabConfig2 = createMockSampleAnalyteLabConfig(null);

//assertEquals("at this point they should be the same", sampleAnalyteLabConfig, sampleAnalyteLabConfig2);
mockMvc.perform(post("/api/lab/sampleanalytelab")
.
contentType(MediaType.APPLICATION_JSON_UTF8)
.
content(getBytes(sampleAnalyteLabConfig))).andExpect(status().isCreated());

//verify(sampleAnalyteLabConfigService, times(1)).save(sampleAnalyteLabConfig2);
   verify(sampleAnalyteLabConfigService, times(1)).saved(sampleAnalyteLabConfig);
}
abstract static class MockSampleAnalyteService implements SampleAnalyteService { @Override public long save(Config config) { config.setId(newInt); saved(config); return newInt; } abstract void saved(Config config); }

(The blue-colored code are critical)

Using @Spy separates side-effect/invariants from interaction verification. The normal save() method does the former, and the abstract saved() method does the latter.

Although, I kinda wonder the point of doing the side-effect inside the Answer. I can understand if the subject-under-test does side-effects. But why do you need to set the id from a mock, especially if it makes testing harder?




Reply all
Reply to author
Forward
0 new messages