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

如何使用MSTest结合Moq模拟EF5 Linq查询以测试AuditRepository方法

实现方案

前置依赖安装

首先安装测试所需的NuGet包:

  • MSTest.TestFramework
  • MSTest.TestAdapter
  • Moq
  • EntityFramework(版本对应你使用的EF5版本)

前提说明

我们先对代码中缺失的定义做合理假设,你可以根据自己的实际业务调整:

  1. 存在Setting实体类对应配置表,结构如下:
public class Setting
{
    public string Name { get; set; }
    public bool Value { get; set; }
}
  1. SQLDBContext中的settings属性为public virtual ObjectSet<Setting> settings { get; set; }
  2. AddAuditData方法返回值为void,Audit和AuditData为业务实体类
  3. SQLDBContext的AddObject、SaveChanges方法为virtual(Moq仅能模拟虚成员/接口成员)

测试逻辑实现

我们一共需要覆盖2种核心场景:

  1. 审计开关开启时,验证审计数据会被添加并保存
  2. 审计开关关闭时,验证不会执行任何添加、保存操作

完整测试代码示例

using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Data.Entity.Core.Objects; // EF5的ObjectContext命名空间,根据实际版本调整
using System.Linq;

[TestClass]
public class AuditRepositoryTests
{
    /// <summary>
    /// 测试场景:审计开关开启时,正确添加保存审计数据
    /// </summary>
    [TestMethod]
    public void AddAuditData_WhenAuditEnabled_IsTrue_AddsAndSavesData()
    {
        // 1. 模拟settings数据集,构造开启审计的测试数据
        var testSettings = new List<Setting>
        {
            new Setting { Name = "AuditEnabled", Value = true }
        }.AsQueryable();

        // 2. 模拟ObjectSet<Setting>,实现IQueryable接口支持Linq查询
        var mockSettingSet = new Mock<ObjectSet<Setting>>();
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.Provider)
            .Returns(testSettings.Provider);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.Expression)
            .Returns(testSettings.Expression);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.ElementType)
            .Returns(testSettings.ElementType);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.GetEnumerator())
            .Returns(testSettings.GetEnumerator());

        // 3. 模拟SQLDBContext
        var mockContext = new Mock<SQLDBContext>();
        mockContext.Setup(c => c.settings).Returns(mockSettingSet.Object);

        // 4. 构造被测类实例
        var auditRepository = new AuditRepository(mockContext.Object);
        var testAuditData = new Audit();

        // 5. 执行被测方法
        auditRepository.AddAuditData(testAuditData);

        // 6. 验证结果:AddObject和SaveChanges都被调用1次
        mockContext.Verify(c => c.AddObject(nameof(AuditData), It.Is<Audit>(a => a == testAuditData)), Times.Once);
        mockContext.Verify(c => c.SaveChanges(), Times.Once);
    }

    /// <summary>
    /// 测试场景:审计开关关闭时,不执行任何操作
    /// </summary>
    [TestMethod]
    public void AddAuditData_WhenAuditEnabled_IsFalse_DoNothing()
    {
        // 1. 构造关闭审计的测试数据
        var testSettings = new List<Setting>
        {
            new Setting { Name = "AuditEnabled", Value = false }
        }.AsQueryable();

        // 2. 模拟ObjectSet<Setting>
        var mockSettingSet = new Mock<ObjectSet<Setting>>();
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.Provider)
            .Returns(testSettings.Provider);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.Expression)
            .Returns(testSettings.Expression);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.ElementType)
            .Returns(testSettings.ElementType);
        mockSettingSet.As<IQueryable<Setting>>()
            .Setup(s => s.GetEnumerator())
            .Returns(testSettings.GetEnumerator());

        // 3. 模拟SQLDBContext
        var mockContext = new Mock<SQLDBContext>();
        mockContext.Setup(c => c.settings).Returns(mockSettingSet.Object);

        // 4. 构造被测类并执行方法
        var auditRepository = new AuditRepository(mockContext.Object);
        auditRepository.AddAuditData(new Audit());

        // 5. 验证结果:没有调用任何添加、保存方法
        mockContext.Verify(c => c.AddObject(It.IsAny<string>(), It.IsAny<object>()), Times.Never);
        mockContext.Verify(c => c.SaveChanges(), Times.Never);
    }
}

常见问题说明

  • 如果你实际代码中isAuditEnabled的取值逻辑是取FirstOrDefault而不是直接返回集合,只需要调整测试数据的构造即可,模拟的IQueryable会自动处理Linq查询逻辑
  • 如果SQLDBContext的成员没有标记为virtual,可以额外定义一个ISQLDBContext接口抽取需要的成员,让SQLDBContext实现该接口,之后直接模拟ISQLDBContext即可,无需修改原有上下文类的代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 10:18:01