ジャクソン/ SpringBootでのテストカスタムJsonDeserializer

タチアナは教育します:

私は@JsonDeserializeが付い@Autowiredパラメータと私のエンティティとコンストラクタを使用してインスタンス化されたカスタムデシリアライザに単体テストを記述しようとしています。それはMockMvcは春のサーバーサイドを立ち上げる私の統合テストで正常に動作します。

しかしobjectMapper.readValueは(...)と呼ばれているテストで、パラメータなしでデフォルトコンストラクタを使用してデシリアライザの新しいインスタンスがインスタンス化されます。たとえ

@Bean
public MyDeserializer deserializer(ExternalObject externalObject) 

デシリアライザの有線バージョンをインスタンス化し、実際の呼び出しは、まだ空のコンストラクタとコンテキストに渡されるがいっぱいになっていません。

私は手動でデシリアライザインスタンスのインスタンス化とObjectMapperでそれを登録しようとしたが、私は私のエンティティクラスから@JsonDeserializeを削除する場合にのみ動作します(と私は私の@Configurationクラスで同じことをしても、それは私の統合テストを中断します。) -ルックス関連します:これにhttps://github.com/FasterXML/jackson-databind/issues/1300

私はまだ直接(...)deserializer.deserializeを呼び出すデシリアライザの動作をテストすることができますが、このアプローチは、デシリアライザのユニットテストではありませんテストで私のために動作しません...

UPD:以下の作業コード

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.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.cfg.HandlerInstantiator;
import com.github.tomakehurst.wiremock.common.Json;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.json.JsonTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

import java.io.IOException;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

@JsonTest
@RunWith(SpringRunner.class)
public class JacksonInjectExample {
    private static final String JSON = "{\"field1\":\"value1\", \"field2\":123}";

    public static class ExternalObject {
        @Override
        public String toString() {
            return "MyExternalObject";
        }
    }

    @JsonDeserialize(using = MyDeserializer.class)
    public static class MyEntity {
        public String field1;
        public String field2;
        public String name;

        public MyEntity(ExternalObject eo) {
            name = eo.toString();
        }

        @Override
        public String toString() {
            return name;
        }
    }

    @Component
    public static class MyDeserializer extends JsonDeserializer<MyEntity> {

        @Autowired
        private ExternalObject external;

        public MyDeserializer() {
            SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
        }

        public MyDeserializer(@JacksonInject final ExternalObject external) {
            this.external = external;
        }

        @Override
        public MyEntity deserialize(JsonParser p, DeserializationContext ctxt) throws IOException,
            JsonProcessingException {
            return new MyEntity(external);
        }
    }

    @Configuration
    public static class TestConfiguration {
        @Bean
        public ExternalObject externalObject() {
            return new ExternalObject();
        }

        @Bean
        public MyDeserializer deserializer(ExternalObject externalObject) {
            return new MyDeserializer(externalObject);
        }
    }

    @Test
    public void main() throws IOException {
        HandlerInstantiator hi = mock(HandlerInstantiator.class);
        MyDeserializer deserializer = new MyDeserializer();
        deserializer.external = new ExternalObject();
        doReturn(deserializer).when(hi).deserializerInstance(any(), any(), eq(MyDeserializer.class));
        final ObjectMapper mapper = Json.getObjectMapper();
        mapper.setHandlerInstantiator(hi);

        final MyEntity entity = mapper.readValue(JSON, MyEntity.class);
        Assert.assertEquals("MyExternalObject", entity.name);
    }
}
ジョーンHorstmann:

非常に興味深い質問、それは私がジャクソンデシリアライザにオートワイヤリングが実際に春のアプリケーションでどのように機能するか疑問に思うしました。使用されているジャクソン施設があるように思わHandlerInstantiatorインタフェースされ、にバネで構成されたSpringHandlerInstantiator実装だけでアプリケーションのコンテキストでクラスを検索します。

だから、理論的には、あなたは、セットアップANができObjectMapper、あなた自身とあなたのユニットテストでは(嘲笑)をHandlerInstantiatorから準備されたインスタンスを返します、deserializerInstance()返すために罰金であると考えられるnull他の方法またはクラス・パラメータが一致しない場合、これはジャクソンが独自にインスタンスを作成します。

しかし、私は、これはユニットテストのデシリアライゼーション・ロジックには良い方法だとは思わないObjectMapperセットアップは、実際のアプリケーションの実行中に使用されるものとは必ずしも異なっています。使用JsonTestアントンの答えで提案されているよう注釈を使用すると、実行時に使用されるのと同じJSONの設定を取得しているとして、よりよい方法でしょう。

おすすめ

転載: http://10.200.1.11:23101/article/api/json?id=478096&siteId=1