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

如何为IValidatableObject的Validate方法编写xUnit单元测试?

为实现IValidatableObject的Settings类编写xUnit单元测试

首先补全你代码中用到的Category枚举示例(假设定义如下):

public enum Category
{
    TypeA,
    TypeB
}

测试场景与实现

我们需要覆盖两类验证逻辑:数据注解([Required]标注的Id字段)和自定义Validate方法中的枚举合法性校验。以下是完整的xUnit测试类:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Xunit;

public class SettingsTests
{
    // 测试Id字段的Required验证
    [Fact]
    public void Settings_WithZeroId_FailsRequiredValidation()
    {
        // 准备测试对象
        var settings = new Settings { Id = 0, Category = Category.TypeA };
        var validationContext = new ValidationContext(settings);
        var validationResults = new List<ValidationResult>();

        // 执行验证
        var isValid = Validator.TryValidateObject(
            settings, 
            validationContext, 
            validationResults, 
            validateAllProperties: true);

        // 断言结果
        Assert.False(isValid);
        Assert.Contains(validationResults, result => result.MemberNames.Contains(nameof(Settings.Id)));
        Assert.Contains(validationResults, result => result.ErrorMessage?.Contains("required") ?? false);
    }

    // 测试合法枚举值的自定义验证
    [Fact]
    public void Validate_WithValidCategory_ReturnsNoErrors()
    {
        // 准备测试对象
        var settings = new Settings { Id = 1, Category = Category.TypeB };
        var validationContext = new ValidationContext(settings);

        // 执行自定义验证
        var validationResults = settings.Validate(validationContext).ToList();

        // 断言结果
        Assert.Empty(validationResults);
    }

    // 测试非法枚举值的自定义验证
    [Fact]
    public void Validate_WithInvalidCategory_ReturnsError()
    {
        // 准备测试对象:使用未定义的枚举值
        var settings = new Settings { Id = 1, Category = (Category)999 };
        var validationContext = new ValidationContext(settings);

        // 执行自定义验证
        var validationResults = settings.Validate(validationContext).ToList();

        // 断言结果
        Assert.Single(validationResults);
        var errorResult = validationResults.First();
        Assert.Equal("Invalid Category", errorResult.ErrorMessage);
        Assert.Contains(nameof(Settings.Category), errorResult.MemberNames);
    }

    // 组合验证:同时触发数据注解和自定义验证错误
    [Fact]
    public void Settings_WithInvalidIdAndCategory_ReturnsTwoErrors()
    {
        // 准备测试对象
        var settings = new Settings { Id = 0, Category = (Category)999 };
        var validationContext = new ValidationContext(settings);
        var validationResults = new List<ValidationResult>();

        // 执行全量验证
        var isValid = Validator.TryValidateObject(
            settings, 
            validationContext, 
            validationResults, 
            validateAllProperties: true);

        // 断言结果
        Assert.False(isValid);
        Assert.Equal(2, validationResults.Count);
        Assert.Contains(validationResults, r => r.MemberNames.Contains(nameof(Settings.Id)));
        Assert.Contains(validationResults, r => r.MemberNames.Contains(nameof(Settings.Category)));
    }
}

关于ValidationContext的说明

你的Validate方法中没有使用ValidationContext的任何属性(比如服务提供者、附加项等),因此不需要模拟,直接通过new ValidationContext(settings)实例化即可。如果后续你的验证逻辑需要依赖ValidationContext中的服务(例如从DI容器获取依赖),再考虑使用Moq等框架模拟ValidationContext的相关属性,当前场景完全不需要额外模拟。

内容的提问来源于stack exchange,提问作者sam sergiy klok

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 20:03:27