Reputation: 177
I want to write an unit test to test a JsonProcessingException. This exception can happen on the line: mapper.writeValueAsString().
public void myMethod(Args...) {
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
Document.parse(mapper.writeValueAsString(testObject)));
}
} catch (JsonProcessingException e) {
log.error("Error parsing the object to json string. ", e);
}
}
and here is my unit test:
public class Test {
@Mock
private ObjectMapper mapper;
@InjectMocks
ClassUnderTest sut;
@Test
public void testJsonProcessingException() throws JsonProcessingException {
when(mapper.writeValueAsString(Mockito.any())).thenThrow(new JsonProcessingException("Error parsing the object to json string. "){
private static final long serialVersionUID = 1L;});
sut.myMethod(args...);
}
}
The problem is the mapper which i have mocked will not be used (i get unnecessary Mockito stubbings failure) as the mapper is again initialized inside the method. How can i unit test such a situation with mockito?
Upvotes: 6
Views: 18984
Reputation: 21
To test a method using an ObjectMapper and throws a custom exception (wrapping the JsonProcessingException), we need to mock the ObjectMapper and tell it to throw the exception.
Here's a class with a method to parse a JsonNode and throw a custom exception:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class MyService {
private final ObjectMapper objectMapper;
public Boolean processSomething(JsonNode jsonNode) {
Simple simple;
try {
simple = objectMapper.readValue(jsonNode.toString(), Simple.class);
} catch (JsonProcessingException e) {
throw new CustomServerException(e.getMessage(), e);
}
return true;
}
}
To test this method:
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.my.path.MyService;
import org.assertj.core.api.Assertions;
import org.mockito.Mockito;
import org.testng.annotations.Test;
public class TestMyService {
// To test the method for the CustomServerException,
// we need to mock the ObjectMapper to throw the JsonProcessingException:
@Test
void testProcessSomething_JsonProcessingException() {
// using a mock ObjectMapper to force a JsonProcessingException to be thrown
ObjectMapper mockMapper = Mockito.mock(ObjectMapper.class);
MyService serviceWithMockMapper = new MyService(mockMapper);
CustomServerException thrown = assertThrows(CustomServerException.class, () -> {
when(mockMapper.readValue(jsonNode.toString(), Simple.class)).thenThrow(JsonProcessingException.class);
serviceWithMockMapper.processSomething(jsonNode);
});
Assertions.assertNotNull(thrown.getMessage());
}
}
Upvotes: 1
Reputation: 131326
mapper
is not a field/dependency of the instance under test.
It is a local variable created within the method :
public void myMethod() {
try {
ObjectMapper mapper = new ObjectMapper();
...
}
So, @InjectMocks
will be helpless.
I can propose you two alternatives :
1) You could pass mapper
as an argument by changing the signature of the method :
public void myMethod(ObjectMapper mapper) {
...
}
In this way you could pass the mocked ObjectMapper
as argument of the tested method.
If this method is often invoked, passing a boiler plate parameter may be annoying and makes the code less readable.
2) Another way is providing a way to set ObjectMapper
via the constructor of the class under test.
public class ClassUnderTest{
private ObjectMapper objectMapper;
...
public ClassUnderTest(ObjectMapper objectMapper){
this.objectMapper = objectMapper;
}
...
}
Upvotes: 3