问题
I am trying to write a unit test to a custom deserializer that is instantiated using a constructor with an @Autowired parameter and my entity marked with @JsonDeserialize.
public class MyDeserializer extends JsonDeserializer<MyEntity> {
private final MyBean bean;
// no default constructor
@Autowired
public MyDeserializer(MyBean bean){
this.bean = bean
}
...
}
@JsonDeserialize(using = MyDeserializer.class)
public class MyEntity{...}
It works fine in my integration tests where a MockMvc brings up spring serverside.
However with unit tests where I only create ObjectMapper and call mapper.readValue(...), it tries to create a new instance of deserializer using default constructor with no parameters. I tried manually instantiating of a deserializer instance and registering it in ObjectMapper, but it only works if I remove @JsonDeserialize from my entity class (and it breaks my integration tests even if I do the same in my @Configuration class.) - looks related to this: https://github.com/FasterXML/jackson-databind/issues/1300
Test example:
private MyEntity testDeserialize(String jsonFileName){
MyDeserializer deserializer = new MyDeserializer(mock(MyBean.class));
SimpleModule module = new SimpleModule();
module.addDeserializer(MyEntity.class, deserializer);
ObjectMapper mapper = new ObjectMapper()
.registerModule(new Jdk8Module())
.registerModule(module);
return mapper.readValue(this.getClass().getClassLoader().getResource(jsonFileName), MyEntity.class);
}
I can still test the deserializer behavior calling deserializer.deserialize(...) directly, but this approach doesn't work for me in other unit tests that are not Deserializer's unit tests...
Complete code (tried to use this approach for deserializer):
import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
public class JacksonInjectExample {
private static final String JSON = "{\"field1\":\"value1\", \"field2\":123}";
// HttpResponse in your case
public static class ExternalObject {
@Override
public String toString() {
return "MyExternalObject";
}
}
@JsonDeserialize(using = MyDeserializer.class)
public static class MyEntity {
public String name;
// make fields public to avoid writing getters in this example
public String field1;
public int field2;
public MyEntity(ExternalObject eo) {
name = eo.toString();
}
@Override
public String toString() {
return name;
}
}
public static class MyDeserializer extends JsonDeserializer<MyEntity> {
@Autowired
private ExternalObject external;
public MyDeserializer(@JacksonInject final ExternalObject external) {
this.external = external;
}
@Override
public MyEntity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
return new MyEntity(external);
}
}
@Test
public void main() throws IOException {
final ObjectMapper mapper = new ObjectMapper();
final InjectableValues.Std injectableValues = new InjectableValues.Std();
injectableValues.addValue(ExternalObject.class, new ExternalObject());
mapper.setInjectableValues(injectableValues);
final MyEntity bean = mapper.readValue(JSON, MyEntity.class);
System.out.println(bean);
}
}
来源:https://stackoverflow.com/questions/60061131/testing-custom-jsondeserializer