2014-02-11 58 views
11

我有一個類,配置傑克遜ObjectMapper。它增加了一些自定義的串行器和解串器對我的對象類型如下:傑克遜ObjectMapper使用自定義串行器和反串行器

public class JsonMapperFactory { 
    public static ObjectMapper createObjectMapper() { 
     final SimpleModule module = new SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static version")); 
     addCustomDeserializersTo(module); 
     addCustomSerializersTo(module); 

     final ObjectMapper objectMapper = new ObjectMapper(); 
     objectMapper.registerModule(module); 
     return objectMapper; 
    } 
    private static void addCustomSerializersTo(final SimpleModule module) { 
     module.addSerializer(DateTime.class, new DateTimeSerializer()); 
    } 
    private static void addCustomDeserializersTo(final SimpleModule objectMapper) { 
     objectMapper.addDeserializer(DateTime.class, new DateTimeDeserializer()); 
    } 
} 

我已經在這個JsonMapperFactory類測試他們自己的測試類中的測試,我的客戶序列化,所以,我想簡單地檢查創建的ObjectMapper是否具有期望的序列化器(或反序列化器)這可以通過反思ObjectMapper來實現,但似乎沒有任何機制可以做到這一點。

有誰知道的一個很好的方式來測試?

對於解串器,我有以下幾點:

private void assertThatObjectMapperUsesCorrectDeserializer(final Class<?> typeClazz, final Class<?> deserializerClazz) throws JsonMappingException { 
    final DeserializationConfig deserializationConfig = this.objectMapper.getDeserializationConfig(); 
    final JsonDeserializer<Object> deserializer = this.objectMapper.getDeserializerProvider().findTypedValueDeserializer(deserializationConfig, javaTypeFor(typeClazz), null); 
    assertThat(deserializer, is(instanceOf(deserializerClazz))); 
} 
private JavaType javaTypeFor(final Class<?> clazz) { 
    return TypeFactory.type(clazz); //deprecated method :(
} 

這是相當冗長,使用過時的方法。

我還沒有找到一種方法來爲序列化程序做類似的測試。所以,我現在使出序列化對象並檢查其序列正確地(主要是複製串行測試)

任何想法都非常歡迎。

+1

如果你的班級使用了一個構建器,那麼測試會容易得多;這樣你可以傳遞{de,}序列化器的模擬,甚至測試它們序列化到你期望的。 – fge

+0

在您的示例中,com.fasterxml.jackson.databind.type.SimpleType.construct()方法似乎執行與已棄用的TypeFactory.type()相同的功能。 –

+1

事件更好,ObjectMapper.getTypeFactory()返回一個帶有大量工廠「constructSomeType」方法的TypeFactory實例。但是這些類和方法來自傑克遜2.3,所以我假設你仍然使用1.9分支。 –

回答

5

從這裏回答&評論提供,我最近重新設計了這個類,使用ModuleObjectMapper的建設者。這使我可以提供模擬並檢查是否已將正確的(de)序列化器添加到模塊中,然後按預期將模塊註冊到對象映射器。

對象映射生成器:

public class ObjectMapperBuilder { 
    ObjectMapper mapper; 

    public ObjectMapperBuilder configure(final ObjectMapper mapper) { 
     this.mapper = mapper; 
     return this; 
    } 

    public ObjectMapperBuilder withModule(final Module module) { 
     this.mapper.registerModule(module); 
     return this; 
    } 

    public ObjectMapper build() { 
     return this.mapper; 
    } 
} 

模塊生成器:

public class SimpleModuleBuilder { 
    SimpleModule module; 

    public SimpleModuleBuilder configure(final SimpleModule module) { 
     this.module = module; 
     return this; 
    } 

    public <X> SimpleModuleBuilder withSerializer(final Class<X> clazz, final JsonSerializer<X> serializer) { 
     this.module.addSerializer(clazz, serializer); 
     return this; 
    } 

    public <X> SimpleModuleBuilder withDeserializer(final Class<X> clazz, final JsonDeserializer<X> deserializer) { 
     this.module.addDeserializer(clazz, deserializer); 
     return this; 
    } 

    public SimpleModule build() { 
     return this.module; 
    } 
} 

最後,新JsonMapperFactory:

public class JsonMapperFactory { 

    public static ObjectMapper configureObjectMapper(final ObjectMapper mapper, final SimpleModule module) { 
     final SimpleModuleBuilder modulebuilder = new SimpleModuleBuilder(); 

     final SimpleModule configuredModule = modulebuilder.configure(module) 
      .withSerializer(DateTime.class, new DateTimeSerializer()) 
      .withDeserializer(DateTime.class, new DateTimeDeserializer()) 
      .build(); 

     final ObjectMapperBuilder objectMapperBuilder = new ObjectMapperBuilder(); 
     return objectMapperBuilder.configure(mapper).withModule(configuredModule).build(); 
    } 
} 

工廠方法仍是Spring配置中使用,但該配置現在實例化空白ModuleObjectMapper,然後將它們提供給工廠方法,然後配置它們。

1

如果JsonDeserializer(和DateTimeDeserializer也是)是一個接口,您可以輕鬆地「JMock」它,將模擬的實例傳遞給JsonMapperFactory#createObjectMapper,然後準確地調用您的自定義「序列化」方法;例如

DateTimeSerializer serializer = context.mock(DateTimeSerializer.class); 
DateTimeDeserializer serializer = context.mock(DateTimeDeserializer.class); 
ObjectMapper mapper = JacksonMapperFactory.createObjectMapper(deserializer, serializer); 

exactly(1).of(jsonDeserializer).serialize(myDateTime, 
    with(any(JsonGenerator.class), 
    with(any(SerializerProvider.class))) 

作爲一個具體的類,可以改爲定義一個新的(測試範圍)德/串行擴展您的自定義DateTime(反)序列化,並簡單地指望該調用:

private static class DateTimeDeserializerWithCounter extends DateTimeDeserializer { 
    public int counter = 0; 

    @Override 
    public DateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 
     counter++; 
     return super.deserialize(jsonParser, deserializationContext); 
    } 
} 

@Test 
public void usageTest(){ 
    //init mapper with the above DateTimeDeserializerWithCounter - see below 
    mapper.readValue("...", DateTime.class); 
    Assert.assertEquals(1, deserializer.counter); 
} 

下面是一個更「面向測試」工廠的快照:

//package visibility, to allow passing different De/Serializers while testing 
static ObjectMapper createObjectMapper(JsonDeserializer deserializer, JsonSerializer serializer) { 
    final SimpleModule module = new SimpleModule("customerSerializationModule", new Version(1, 0, 0, "static version")); 
    module.addDeserializer(DateTime.class, deserializer); 
    module.addSerializer(DateTime.class, serializer); 

    final ObjectMapper objectMapper = new ObjectMapper(); 
    objectMapper.registerModule(module); 
    return objectMapper; 
} 

//production method: no-args, as in the original version 
public static ObjectMapper createObjectMapper() { 
    return createObjectMapper(new DateTimeDeserializer(), new DateTimeSerializer()); 
} 

希望有幫助。

+0

*我喜歡這個想法*,但我認爲它測試了錯誤的東西。我使用傑克遜作爲一個成熟的圖書館,所以我願意接受它按照我的預期工作。你的解決方案依賴De/Serializing對象並檢查Jackson庫是否調用了我的de/serializers。所以它幾乎在測試傑克遜圖書館。 我不知道是否有方法來反觀ObjectMapper,或者重新設計創建,以允許我測試它是如何創建的。我一直在考慮在測試中被嘲笑的建築師/工廠類。 –

+0

您的解決方案也讓我開始思考重新設計JsonMapperFactory類的想法,這一點我之前沒有考慮過。 –

相關問題