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

C#中如何创建继承Mock的类,无缝复用Setup等Moq功能?

自定义可Mock的SettingsManager实现方案

问题描述

我想创建一个类MockSettingsManager,使其能直接使用Moq框架的Setup等功能,但直接继承Mock不可行。期望实现:初始化时调用CallBase模式下的GetSetting方法,通过Setup设置后返回指定值。以下是我尝试的代码和期望的调用方式:

尝试的类代码

public class MockSettingsManager : Mock // 我知道这样不可行,但想要类似的实现
{
        private static SettingManagerCore singletonSettingManagerCore = null;
        private SettingManagerCore SettingManagerCore
        {
            get
            { 
             if (settingManagerCore== null)
             {
                 Mock<SettingManagerCore> settingManagerCoreMock = new Mock<SettingManagerCore>()
                 {
                   CallBase = true
                 };

                 settingManagerCore = settingManagerMock.Object;
              }

            return singletonSettingManagerCore;
            }
        }

      public virtual T GetSetting<T>(string name)
      {
         return SettingManagerCore.GetSetting<T>(name);
      }
}

期望的调用方式

[TestClass]
public class MyTests
{
    private MockSettingsManager settingsManagerMock = null!;

    [TestInitialize]
    public void Initialize()
    {
        settingsManagerMock = new MockSettingsManager() { CallBase = true };
        // 此调用应走CallBase
        var test = settingsManagerMock.Object.GetSetting<string>("Test");

        // 此调用应返回Test1
        settingsManagerMock.Setup(x => x.GetSetting<string>("Test")).Returns("Test1");
        test = settingsManagerMock.Object.GetSetting<string>("Test");
    }
}

可行解决方案

方案一:基于接口+可Mock子类实现

如果SettingManagerCore未实现接口,建议先抽象出接口,这是最符合Moq设计思想的方案:

  1. 定义接口(已有则跳过)
public interface ISettingsManager
{
    T GetSetting<T>(string name);
}
  1. 创建可Mock的子类
    确保GetSetting是虚方法,或者重写基类的虚方法:
public class MockableSettingsManager : SettingManagerCore, ISettingsManager
{
    public override T GetSetting<T>(string name)
    {
        return base.GetSetting<T>(name);
    }
}
  1. 测试中直接使用Moq
[TestClass]
public class MyTests
{
    private Mock<MockableSettingsManager> _settingsManagerMock = null!;

    [TestInitialize]
    public void Initialize()
    {
        // 开启CallBase,默认走基类实现
        _settingsManagerMock = new Mock<MockableSettingsManager>() { CallBase = true };
        
        // 第一次调用走基类的GetSetting逻辑
        var test = _settingsManagerMock.Object.GetSetting<string>("Test");

        // Setup后返回指定值
        _settingsManagerMock.Setup(x => x.GetSetting<string>("Test")).Returns("Test1");
        test = _settingsManagerMock.Object.GetSetting<string>("Test"); // 此时返回"Test1"
    }
}

方案二:封装Mock实例的自定义类

如果不想依赖接口,可以封装一个包含Mock<SettingManagerCore>实例的类,对外暴露Moq的核心方法:

public class MockSettingsManager
{
    private readonly Mock<SettingManagerCore> _innerMock;

    public MockSettingsManager(bool callBase = true)
    {
        _innerMock = new Mock<SettingManagerCore>() { CallBase = callBase };
    }

    // 暴露目标对象,用于调用业务方法
    public SettingManagerCore Object => _innerMock.Object;

    // 封装Setup方法,对齐Moq的调用方式
    public ISetup<SettingManagerCore, T> Setup<T>(Expression<Func<SettingManagerCore, T>> expression)
    {
        return _innerMock.Setup(expression);
    }

    // 按需封装其他Moq方法,比如Verify
    public void Verify<T>(Expression<Func<SettingManagerCore, T>> expression, Times times)
    {
        _innerMock.Verify(expression, times);
    }
}

测试调用方式和你期望的几乎一致:

[TestClass]
public class MyTests
{
    private MockSettingsManager _settingsManagerMock = null!;

    [TestInitialize]
    public void Initialize()
    {
        _settingsManagerMock = new MockSettingsManager(callBase: true);
        
        // 走CallBase模式下的基类逻辑
        var test = _settingsManagerMock.Object.GetSetting<string>("Test");

        // Setup后返回指定值
        _settingsManagerMock.Setup(x => x.GetSetting<string>("Test")).Returns("Test1");
        test = _settingsManagerMock.Object.GetSetting<string>("Test"); // 返回"Test1"
    }
}

关键说明

  • Moq的Mock<T>是泛型类,无法直接非泛型继承,所以初始思路不可行,必须通过封装或基于含虚方法的类/接口实现。
  • Moq能拦截方法调用的前提是:目标方法为虚方法,或者类实现了对应的接口,否则无法生成代理进行拦截。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 13:51:07