You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为用于JSON反序列化的DTO类添加代码覆盖率测试?

如何为用于JSON反序列化的DTO类添加代码覆盖率测试?

兄弟,我懂你这种困惑!刚接触测试的时候,看到DTO这种全是属性的类,真的会懵——这玩意儿有啥好测的?但其实SonarCloud提示的覆盖率问题,刚好是个契机,让我们顺便验证JSON序列化/反序列化的正确性,一举两得!

下面给你几个实用的测试方向,都是针对你这种带JsonDerivedType的DTO场景的:

1. 测试JSON反序列化到对应子类的正确性

这是最核心的测试,既能覆盖DTO的所有属性,又能验证你加的JsonDerivedType特性是否生效——毕竟类型判别器要是写错了,反序列化直接就炸了。

举个xUnit的测试例子,你可以直接套用到你的代码里:

using Xunit;
using System.Text.Json;

public class VehicleDtoDeserializationTests
{
    [Fact]
    public void TrolleyDto_Deserialize_FromValidJson_MapsAllPropertiesCorrectly()
    {
        // 构造符合TrolleyDto结构的JSON,注意要带上typeDiscriminator
        var trolleyJson = @"
        {
            ""$type"": ""trolley"",
            ""Name"": ""Cargo Trolley 001"",
            ""CreationDate"": ""2024-05-20"",
            ""ExtendedData"": {
                ""Capacity"": 150,
                ""WheelsCount"": 4
            }
        }";

        // 执行反序列化
        var vehicle = JsonSerializer.Deserialize<VehicleBaseDto>(trolleyJson, new JsonSerializerOptions
        {
            PropertyNameCaseInsensitive = true // 适配JSON的大小写问题
        });

        // 先验证反序列化出来的是正确的子类
        Assert.IsType<TrolleyDto>(vehicle);
        var trolley = (TrolleyDto)vehicle;

        // 验证基础属性
        Assert.Equal("Cargo Trolley 001", trolley.Name);
        Assert.Equal("2024-05-20", trolley.CreationDate);

        // 验证ExtendedData的属性(假设TrolleyOptions有这两个属性)
        Assert.NotNull(trolley.ExtendedData);
        Assert.Equal(150, trolley.ExtendedData.Capacity);
        Assert.Equal(4, trolley.ExtendedData.WheelsCount);
    }
}

2. 测试序列化DTO到JSON的正确性

反过来,把DTO实例序列化成JSON,既能覆盖属性的getter方法(提升覆盖率),又能确认输出的JSON包含正确的字段和类型判别器,避免前端或者下游服务解析出错。

示例代码:

[Fact]
public void ScooterDto_Serialize_ToJson_IncludesAllRequiredFields()
{
    // 创建一个ScooterDto实例
    var scooter = new ScooterDto
    {
        Name = "City Scooter 007",
        CreationDate = "2024-05-21",
        ExtendedData = new ScooterOptions
        {
            MaxSpeed = 30,
            BatteryLevel = 85
        }
    };

    // 序列化成JSON
    var jsonOutput = JsonSerializer.Serialize<VehicleBaseDto>(scooter);

    // 验证输出的JSON包含必要内容
    Assert.Contains(@"Name"":""City Scooter 007""", jsonOutput);
    Assert.Contains(@"CreationDate"":""2024-05-21""", jsonOutput);
    Assert.Contains(@"$type"":""scooter""", jsonOutput); // 确认类型判别器正确输出
    Assert.Contains(@"MaxSpeed"":30", jsonOutput);
    Assert.Contains(@"BatteryLevel"":85", jsonOutput);
}

3. 测试边界情况(默认值、空值)

你的DTO里Name属性有默认值string.Empty,这种细节也可以测,既能覆盖属性的初始化逻辑,又能验证反序列化时的默认值是否生效。

示例:

[Fact]
public void VehicleBaseDto_Deserialize_MissingNameField_UsesDefaultEmptyString()
{
    var baseVehicleJson = @"
    {
        ""$type"": ""base"",
        ""CreationDate"": ""2024-05-22""
    }";

    var result = JsonSerializer.Deserialize<VehicleBaseDto>(baseVehicleJson);

    // 验证Name属性用了默认值
    Assert.Equal(string.Empty, result.Name);
    Assert.Equal("2024-05-22", result.CreationDate);
}

小技巧:用理论测试减少重复代码

如果每个子类都写一遍类似的测试会很繁琐,你可以用xUnit的Theory加InlineData,把不同的JSON字符串、期望类型、验证逻辑打包成参数,一次测试覆盖所有子类:

[Theory]
[InlineData(@"{""$type"":""scooter"",""Name"":""Test Scooter"",""CreationDate"":""2024-05-20""}", typeof(ScooterDto))]
[InlineData(@"{""$type"":""forklift"",""Name"":""Test Forklift"",""CreationDate"":""2024-05-20""}", typeof(ForkliftDto))]
public void VehicleDto_Deserialize_CorrectlyMapsToSubtype(string json, Type expectedType)
{
    var result = JsonSerializer.Deserialize<VehicleBaseDto>(json);
    Assert.IsType(expectedType, result);
    Assert.Equal("Test " + expectedType.Name.Replace("Dto", ""), result.Name);
}

这样既省了代码,又能一次性覆盖所有子类的类型判别器逻辑,覆盖率直接拉满!

备注:内容来源于stack exchange,提问作者wutangcodeclan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 09:23:08