orcl user
orcl user

Reputation: 177

Unit test for JsonProcessingException

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

Answers (2)

AJW
AJW

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

davidxxx
davidxxx

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

Related Questions