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

如何结合Ardalis Specification与NSubstitute按ID匹配仓储模拟?

解决Ardalis Specification与NSubstitute按ID匹配的问题

方案一:给Specification添加公共只读ID属性(推荐)

首先修改ProductByIdSpec,将构造函数传入的ID暴露为只读公共属性,既不破坏封装性,又能方便测试匹配:

public sealed class ProductByIdSpec : SingleResultSpecification<Product>
{
    // 新增只读公共属性,暴露传入的ID
    public int Id { get; }

    public ProductByIdSpec(int id)
    {
        Id = id;
        Query.Where(p => p.Id == id);
    }
}

之后在单元测试中,使用Arg.Is的Lambda重载,直接检查Specification的Id属性值:

// 设置模拟仓储的返回规则:当传入Id为42的ProductByIdSpec时,返回对应Product
_productRepository
    .SingleOrDefaultAsync(Arg.Is<ProductByIdSpec>(spec => spec.Id == 42))
    .ReturnsAsync(new Product { Id = 42, Name = "测试产品" });

// 调用测试代码
var result = await _productRepository.SingleOrDefaultAsync(new ProductByIdSpec(42));
// result会返回预设的Product实例

方案二:重写Specification的Equals和GetHashCode方法

如果不想暴露公共属性,可以通过重写Equals和GetHashCode,让两个ProductByIdSpec实例只要ID相同就判定为相等:

public sealed class ProductByIdSpec : SingleResultSpecification<Product>
{
    private readonly int _id;

    public ProductByIdSpec(int id)
    {
        _id = id;
        Query.Where(p => p.Id == id);
    }

    public override bool Equals(object obj)
    {
        // 仅当obj是ProductByIdSpec且ID相同时,判定为相等
        return obj is ProductByIdSpec otherSpec && otherSpec._id == _id;
    }

    public override int GetHashCode()
    {
        // 基于ID生成哈希码
        return HashCode.Combine(_id);
    }
}

此时测试中直接使用Arg.Is(new ProductByIdSpec(42))就能匹配:

_productRepository
    .SingleOrDefaultAsync(Arg.Is(new ProductByIdSpec(42)))
    .ReturnsAsync(new Product { Id = 42 });

方案三:通过反射匹配(不推荐)

如果无法修改ProductByIdSpec的代码,可以用反射读取私有字段的ID值,但这种方式依赖编译器生成的字段名,稳定性差,仅作为临时方案:

using System.Reflection;

// 设置模拟返回值
_productRepository
    .SingleOrDefaultAsync(Arg.Is<ProductByIdSpec>(spec => 
    {
        // 通过反射获取构造函数传入的私有ID字段值
        var idField = spec.GetType().GetField("<Id>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
        return idField != null && (int)idField.GetValue(spec) == 42;
    }))
    .ReturnsAsync(new Product { Id = 42 });

内容的提问来源于stack exchange,提问作者Mathias F

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 09:20:07