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

如何正确Mock EF Core中的FromSqlInterpolated扩展方法?

错误原因

你遇到的报错是因为FromSqlInterpolated是EF Core提供的静态扩展方法,Moq框架不支持直接对静态扩展方法做Setup/校验,所有静态扩展方法的直接Mock都会触发这个NotSupportedException。

解决方案

方案1:业务层做薄抽象(推荐)

该方案对业务侵入性极低,后续维护成本也更低,只需要把存储过程调用逻辑封装为Repo层的虚方法即可:
首先修改业务代码:

// 你的Repo类中新增虚方法,专门封装存储过程调用逻辑
public virtual List<UserTag> GetUserTagFromSp(string userId)
{
    return this.context.UserTag.FromSqlInterpolated($"ctc_sp_UserTag {userId}").ToList();
}

// 原有PrintUserTag方法改为调用上述封装好的虚方法
public List<UserTag> PrintUserTag(string userId)
{
    return GetUserTagFromSp(userId);
}

之后测试代码直接Mock这个自定义的虚方法即可,不需要接触EF Core的底层API:

[Fact]
public void UserTagTest_ReturnsSuccessResult()
{
    string userId = "7C8FFA01-94FC-42EA-9C43-5CA7FD0400D1";
    List<UserTag> userTags = new List<UserTag>()
    {
        new UserTag()
        {
            Id = userId,
            UserTagName = "TestName",
        }
    };

    // 直接Mock你自己定义的Repo方法即可
    var mockRepo = new Mock<你的Repo类名>();
    mockRepo.Setup(x => x.GetUserTagFromSp(userId)).Returns(userTags);
    var actual = mockRepo.Object.PrintUserTag(userId);

    Assert.NotNull(actual);
    // 其余校验逻辑
}

方案2:Mock底层IQueryProvider(无需修改业务代码)

如果不想调整现有业务代码,可以直接Mock EF Core扩展方法底层依赖的IQueryProvider接口,所有LINQ及EF扩展方法最终都会调用该接口的CreateQuery方法执行逻辑:
测试代码调整如下:

[Fact]
public void UserTagTest_ReturnsSuccessResult()
{
    string userId = "7C8FFA01-94FC-42EA-9C43-5CA7FD0400D1";
    List<UserTag> userTags = new List<UserTag>()
    {
        new UserTag()
        {
            Id = userId,
            UserTagName = "TestName",
        }
    }.AsQueryable().ToList();

    // 1. Mock IQueryProvider,拦截FromSqlInterpolated的调用
    var mockQueryProvider = new Mock<IQueryProvider>();
    mockQueryProvider
        .Setup(p => p.CreateQuery<UserTag>(It.IsAny<MethodCallExpression>()))
        .Returns(userTags.AsQueryable());

    // 2. 构造带Mock Provider的DbSet
    var mockDbSet = new Mock<DbSet<UserTag>>();
    mockDbSet.As<IQueryable<UserTag>>().Setup(m => m.Provider).Returns(mockQueryProvider.Object);
    mockDbSet.As<IQueryable<UserTag>>().Setup(m => m.Expression).Returns(userTags.AsQueryable().Expression);
    mockDbSet.As<IQueryable<UserTag>>().Setup(m => m.ElementType).Returns(userTags.AsQueryable().ElementType);
    mockDbSet.As<IQueryable<UserTag>>().Setup(m => m.GetEnumerator()).Returns(userTags.GetEnumerator());

    // 3. 正常Setup Context的DbSet属性
    var mockContext = new Mock<你的DbContext类名>();
    mockContext.Setup(x => x.UserTag).Returns(mockDbSet.Object);

    // 4. 后续执行逻辑与校验保持不变
    var repo = new 你的Repo类名(mockContext.Object);
    var actual = repo.PrintUserTag(userId);

    Assert.NotNull(actual);
    // 其余校验逻辑
}

如果需要校验传入的SQL语句和参数是否正确,可以调整IQueryProvider的Setup条件,解析MethodCallExpression中传入的FormattableString参数做匹配即可。

内容的提问来源于stack exchange,提问作者Andryano Feumty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 23:27:03