忽略单元测试的 Jackon JsonProperty 访问

Posted

技术标签:

【中文标题】忽略单元测试的 Jackon JsonProperty 访问【英文标题】:Ignoring Jackon JsonProperty Access for Unit Tests 【发布时间】:2017-08-04 03:32:36 【问题描述】:

我在我的 Spring Boot 项目中使用 Jackson 进行序列化/反序列化。

我有一个具有以下结构的 DTO 对象,

public class TestDTO implements Serializable 
    private static final long serialVersionUID = 1L;

    private Long id;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private UUID certificateId;

    @NotNull
    private Long orgId;

    @NotNull
    private CertificateType certificateType;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Valid
    @NotNull
    private PublicCertificateDTO publicCertificate;

    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    @Valid
    private PrivateCertificateDTO privateCertificate;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private ZonedDateTime expiryDate;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private ZonedDateTime createdDate;

    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
    private ZonedDateTime updatedDate;

在我的单元测试中使用以下方法序列化这个对象,

public static byte[] convertObjectToJsonBytes(TestDTO object)
        throws IOException 
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    JavaTimeModule module = new JavaTimeModule();
    mapper.registerModule(module);

    return mapper.writeValueAsBytes(object);

导致具有WRITE_ONLY 访问权限的字段被忽略(原因很明显)。所以在序列化对象中,我看到了 publicCertificateprivateCertificate 的空值。

我确实尝试设置mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)

还有其他方法可以忽略单元测试的这些属性吗?

【问题讨论】:

【参考方案1】:

虽然指定的解决方案有效,但它对要求来说是多余的。如果您只想覆盖注释,则不需要自定义序列化程序。 Jackson 有一个mixin feature 来满足这些琐碎的要求

考虑以下简化的 POJO:

public class TestDTO

    public String regularAccessProperty;
    @JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
    public String writeAccessProperty;

如果您想覆盖 @JsonProperty 注释,您可以创建另一个 POJO,该 POJO 的变量具有完全相同的名称(或相同的 getter/setter 名称):

// mixin class that overrides json access annotation
public class UnitTestDTO

    @JsonProperty(access = JsonProperty.Access.READ_WRITE)
    public String writeAccessProperty;

你通过一个 Simplemodule 将原始 POJO 和 mixin 关联起来:

simpleModule.setMixInAnnotation(TestDTO.class, UnitTestDTO.class);

【讨论】:

将您的解决方案标记为解决方案,因为它不那么麻烦 无需重写JsonProperty注解,使用mixin功能,只需拥有相同字段的重复DTO,且重复DTO的字段不定义任何JsonProperty注解。 @YogaGowda,您的评论不清楚。 “重复的 DTO”是什么意思 - 具有相同字段和不同名称的类? - 这是一个混合 嗨 @SharonBenAsher,是的,并且没有在重复的 DTO 中的字段上定义任何 JsonProperty @YogaGowda 我不明白这是怎么回事,也许你可以举个例子来启发我们【参考方案2】:

还有其他方法可以忽略单元测试的这些属性吗?

解决方案:在您的convertObjectToJsonBytes 方法中,您可以使用:

mapper.disable(MapperFeature.USE_ANNOTATIONS);

参考:MapperFeature.USE_ANNOTATIONS

/**
 * Feature that determines whether annotation introspection
 * is used for configuration; if enabled, configured
 * @link AnnotationIntrospector will be used: if disabled,
 * no annotations are considered.
 *<p>
 * Feature is enabled by default.
 */
USE_ANNOTATIONS(true),

注意:这将禁用给定ObjectMapper的所有注释。

【讨论】:

我喜欢这个!【参考方案3】:

通过为 JUnit 测试添加自定义序列化程序解决了这个问题。

所以对于TestDTO,我添加了如下的序列化器。

private class TestJsonSerializer extends StdSerializer<TestDTO> 
    public TestJsonSerializer() 
        this(null);
    

    public TestJsonSerializer(Class<TestDTO> t) 
        super(t);
    

    @Override
    public void serialize(TestDTO value, JsonGenerator gen, SerializerProvider provider) throws IOException 
        gen.writeStartObject();
        gen.writeNumberField("orgId", value.getOrgId());
        gen.writeStringField("certificateType", value.getCertificateType().getType());
        if (value.getPublicCertificate() != null) 
            gen.writeObjectField("publicCertificate", value.getPublicCertificate());
        
        if (value.getPrivateCertificate() != null) 
            gen.writeObjectField("privateCertificate", value.getPrivateCertificate());
        
        gen.writeObjectField("expiryDate", value.getExpiryDate());
        gen.writeObjectField("createdDate", value.getCreatedDate());
        gen.writeObjectField("updatedDate", value.getUpdatedDate());
        gen.writeEndObject();
    

然后我补充说,

ObjectMapper mapper = new ObjectMapper();
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(TestDTO.class, new TestJsonSerializer());
mapper.registerModule(simpleModule);

为嵌套对象 publicCertificateprivateCertificate 添加并注册了自定义序列化程序。

【讨论】:

【参考方案4】:

这里是一个简单的例子

@ToString
@Getter
@Setter
public class Account implements Cloneable 

    @JsonProperty(access = Access.WRITE_ONLY)
    private Integer accountId;
    private String accountType;
    private Long balance;

public AccountTest clone() 
    AccountTest test = new AccountTest();
    test.setAccountId(this.accountId);
    test.setAccountType(this.accountType);
    test.setBalance(this.balance);
    return test;

@ToString
@Getter
@Setter
public class AccountTest 

    private Integer accountId;
    private String accountType;
    private Long balance;


    public static void main(String[] args) 
              ObjectMapper mapper = new ObjectMapper();
    try 
        Account account = new Account();
        account.setAccountId(1999900);
        account.setAccountType("Saving");
        account.setBalance(2433l);
        AccountTest accountTest = account.clone();
        System.out.println(account);

        byte[] accountBytes = mapper.writeValueAsBytes(account);
        System.out.println(new String(accountBytes));

        byte[] accountTestBytes = mapper.writeValueAsBytes(accountTest);
        System.out.println(new String(accountTestBytes));
     catch (IOException e)  

    

【讨论】:

请用示例代码重新阅读问题。问题是关于序列化而不是反序列化 嘿@Sharon,我错过了,我们仍然可以在没有mixin的情况下实现序列化,看看更新的示例代码,添加克隆方法只是为了便于测试。不需要克隆。 所以这个解决方案需要创建一个具有 all 原始属性的相同类并复制它们,这需要额外的开发(克隆可能并不总是足够)以及运行时性能影响。我的解决方案需要创建一个仅包含需要更改 json 注释的属性的类。就是这样,没有克隆或复制。只有 json 注释的“成本”。至少每种解决方案各有利弊。 现在,想象一个有 200 个属性的类。在我的解决方案中,很明显需要 mixin 的区别是什么。在您的解决方案中,路过的开发人员很难收集两个庞然大物类之间的差异。此外,想象一个随着每个软件版本而变化的类,随着产品需求的变化添加/删除属性。您的解决方案需要每次都复制这些更改。坦率地说,我根本看不到您的解决方案的优势。

以上是关于忽略单元测试的 Jackon JsonProperty 访问的主要内容,如果未能解决你的问题,请参考以下文章

Python:如何在单元(鼻子)测试期间忽略装饰器?

Angular 单元测试 - 忽略样式表

Sqlite 在单元测试中忽略数据库约束

Spring Boot 单元测试忽略 logging.level

python覆盖模块可以有条件地忽略单元测试中的行吗?

如何忽略 Next.js 组件中导致单元测试中的解析错误的 CSS 模块导入