The code for my usecase is below:
My class:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class MyClass {
public String getString() {
// object need mocking
ObjectMapper mapper = new ObjectMapper()
try {
// method need mocking
return mapper.writeValueAsString(List.of("1", "2"));
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
Test code:
@Test
public void test_get_string() throws Exception {
ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
.thenThrow(JsonProcessingException.class);
Assertions.assertThrows(RuntimeException.class, () -> {
new MyClass().getString();
});
// -> failed
// Expected RuntimeException but nothing was thrown
}
I want to mock the ObjectMapper
and make it throw JsonProcessingException
on writeValueAsString
method call, but the test keeps failing as if it doesn't use my mocked mapper at all. But if I make the mapper a property of MyClass
and mock it, then the test passes.
public class MyClass {
// make mapper a property
private ObjectMapper mapper;
public MyClass(ObjectMapper mapper) {
this.mapper = mapper;
}
public String getString() {
try {
return mapper.writeValueAsString(List.of("1", "2"));
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void test_get_string() throws Exception {
ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
.thenThrow(JsonProcessingException.class);
Assertions.assertThrows(RuntimeException.class, () -> {
// pass mapper on creating object
new MyClass(mapper).getString();
}); // -> success
}
In case I don't want to make mapper a property of my class, how should I properly mock it ?
You can't mock a local variable declared in a method using Mockito. You can make ObjectMapper
a property of your class MyClass
. But if you don't want to do that, the other option is to add a protected method in your MyClass
that returns an instance of ObjectMapper
public class MyClass {
public String getString() {
// object need mocking
ObjectMapper mapper = createObjectMapper()
try {
// method need mocking
return mapper.writeValueAsString(List.of("1", "2"));
}
catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
protected ObjectMapper createObjectMapper() {
return new ObjectMapper();
}
}
Then in your test case, you can spy
your class and stub this new method to return a mocked ObjectMapper
as shown below:
@Test
public void test_get_string() throws Exception {
ObjectMapper mapper = Mockito.mock(ObjectMapper.class);
Mockito.when(mapper.writeValueAsString(Mockito.anyList()))
.thenThrow(JsonProcessingException.class);
MyClass myClass = Mockito.spy(new MyClass ());
doReturn(mapper).when(myClass).createObjectMapper();
Assertions.assertThrows(RuntimeException.class, () -> {
myClass.getString();
});
}