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

使用MSTest与Mocking进行C#单元测试时遇未支持异常求助

Moq Mock设置抛出System.NotSupportedException错误的解决

错误信息

测试方法GUI_VIS.MsTests.LoginTest.TestEmptyIsAdminWindow抛出异常:
System.NotSupportedException: 不支持的表达式: x => x.GetAllEmailAsync("Pepa", "Heslo", new Worker() { })
不可重写的成员(此处为GenericController.GetAllEmailAsync)不能用于设置/验证表达式中。

堆栈跟踪

Guard.IsOverridable(MethodInfo method, Expression expression) 第109行
MethodExpectation.ctor(LambdaExpression expression, MethodInfo method, IReadOnlyList`1 arguments, Boolean exactGenericTypeArguments, Boolean skipMatcherInitialization, Boolean allowNonOverridable) 第87行
ExpressionExtensions.<Split>g__Split|5_0(Expression e, Expression& r, MethodExpectation& p, Boolean assignment, Boolean allowNonOverridableLastProperty) 第219行
ExpressionExtensions.Split(LambdaExpression expression, Boolean allowNonOverridableLastProperty) 第150行
Mock.SetupRecursive[TSetup](Mock mock, LambdaExpression expression, Func`4 setupLast, Boolean allowNonOverridableLastProperty) 第643行
Mock.Setup(Mock mock, LambdaExpression expression, Condition condition) 第498行
Mock`1.Setup[TResult](Expression`1 expression) 第452行
LoginTest.TestEmptyIsAdminWindow() 第48行
ThreadOperations.ExecuteWithAbortSafety(Action action)

测试代码

[TestMethod]
public async Task TestEmptyIsAdminWindow()
{
    Login login = new Login();

    Mock<GenericController> gcMock = new Mock<GenericController>();
    GenericController gc = new GenericController();

    login.IdBoxGet().Text = "John";
    login.PasswordBoxGet().Text = "Password";
    

    ObservableCollection<Worker> workers = new ObservableCollection<Worker>() {
        new Worker
        {
            Name = "John",
            My_password = "Password",
            Role = "Boss"
        }
    };

    gcMock.Setup(x => x.GetAllEmailAsync("John", "Heslo", new Worker() { }))
        .ReturnsAsync(workers);
    
    var workersFromMocking = await gc.GetAllEmailAsync("John", "Password", new Worker() { });


    Assert.IsTrue(workersFromMocking[0].Name == "John");
}

问题分析与解决

1. 核心原因:目标方法不可重写

Moq仅支持mock可重写的成员,包括虚方法(virtual)、抽象方法,或是接口定义的方法。当前GenericController.GetAllEmailAsync是普通非虚方法,Moq无法对其进行Setup操作,这是报错的直接原因。

解决方式二选一:

  • 修改GenericController,将GetAllEmailAsync标记为virtual;
  • 定义接口IGenericController,包含GetAllEmailAsync方法,让GenericController实现该接口,之后mockIGenericController而非具体类。

2. 未使用Mock实例

代码中创建了gcMock,但实际调用方法的是new GenericController()生成的真实实例,完全没有用到mock的配置。必须通过gcMock.Object获取mock实例并使用。

3. 参数不匹配

Setup中的参数是"John", "Heslo", new Worker(),但实际调用时用的是"John", "Password", new Worker(),参数不一致会导致mock的方法不触发。如果不需要严格匹配参数,可以用It.IsAny<string>()、It.IsAny<Worker>()等匹配器。

修改后的测试代码示例

[TestMethod]
public async Task TestEmptyIsAdminWindow()
{
    Login login = new Login();

    // 假设已将GetAllEmailAsync改为虚方法
    Mock<GenericController> gcMock = new Mock<GenericController>();
    // 使用mock对象而非真实实例
    GenericController gc = gcMock.Object;

    login.IdBoxGet().Text = "John";
    login.PasswordBoxGet().Text = "Password";
    

    ObservableCollection<Worker> workers = new ObservableCollection<Worker>() {
        new Worker
        {
            Name = "John",
            My_password = "Password",
            Role = "Boss"
        }
    };

    // 修正参数,或使用匹配器兼容任意Worker实例
    gcMock.Setup(x => x.GetAllEmailAsync("John", "Password", It.IsAny<Worker>()))
        .ReturnsAsync(workers);
    
    var workersFromMocking = await gc.GetAllEmailAsync("John", "Password", new Worker());


    Assert.IsTrue(workersFromMocking[0].Name == "John");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:30:47