문제

I ran into this issue when testing a Spring controller using MockMvc, Mockito and Jackson, so I made a simple class to test out how Jackson behaves. I'm using jackson-databind:2.3.1 and mockito-core:1.9.5.

Given this class:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.Serializable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class Person implements Serializable {
    private String name;
    private int age;

    // Public getters and setters...

    public static void main(String[] args) {
        String name = "Bob";
        int age = 21;
        ObjectMapper objectMapper = new ObjectMapper();

        // attempt serialization with real object
        Person person = new Person();
        person.setName(name);
        person.setAge(age);
        try {
            System.out.println(objectMapper.writeValueAsString(person));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            System.err.println("Failed to serialize real object");
        }

        // attempt serialization with mock object
        Person mockPerson = mock(Person.class);
        when(mockPerson.getName()).thenReturn(name);
        when(mockPerson.getAge()).thenReturn(age);
        try {
            System.out.println(objectMapper.writeValueAsString(mockPerson));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            System.err.println("Failed to serialize mock object.");
        }
    }

Jackson has no problem serializing the real object, however it will throw a JsonMappingException when it tries to serialize the mocked object. Debugging through the code, it's calling serializeFields(bean, jgen, provider) repeatedly, getting stuck on the internal Mockito properties.

So, my question is: Is there anyway to force Jackson to use the getter methods? I tried @JsonIgnoreProperties on the class, @JsonIgnore on the fields, and @JsonProperty on the methods (in different combinations, to no success). Or, do I have to write my own custom serializer?

Thanks!

도움이 되었습니까?

해결책

Here is a solution that will work for you particular case:

First of all you need to create a PersonMixin since you cannot add the required annotations to the mock.

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;


@JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE)
public interface PersonMixin {

    @JsonProperty
    String getName();

    @JsonProperty
    Integer getAge();
}

Now, use the object mapper like the in following code and you will get the same result as when you serialize the real object:

Person mockPerson = mock(Person.class);
when(mockPerson.getName()).thenReturn(name);
when(mockPerson.getAge()).thenReturn(age);
objectMapper.addMixInAnnotations(Person.class, PersonMixin.class);
try {
    System.out.println(objectMapper.writeValueAsString(mockPerson));
} catch (JsonProcessingException e) {
    e.printStackTrace();
    System.err.println("Failed to serialize mock object.");
}

다른 팁

Here's my ObjectMapper that sorted it out without the need for mixins.

The mapper ignores all members of that has "Mockito" in somewhere in their names.

This solution avoids having a mix-in for each serialized object, or annotating code that may not be accessible.

Running the following test succeeds with the output {"name":"Jonh"}.

package test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import org.mockito.Mockito;

public class AppTest extends Mockito {

    public void testApp() throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {

            @Override
            public boolean hasIgnoreMarker(final AnnotatedMember m) {
                return super.hasIgnoreMarker(m) || m.getName().contains("Mockito");
            }
        });

        final String name = "Jonh";

        Person mockPerson = mock(Person.class);
        when(mockPerson.getName()).thenReturn(name);
        System.out.println(mapper.writeValueAsString(mockPerson));
    }


    public static class Person {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top