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

使用Moq与XUnit单元测试时如何仅Mock同一类中的两个指定方法

实现步骤

1. 修改服务类代码,给待Mock的方法加virtual修饰

Moq只能重写带有virtual标识的类方法,所以你需要把GetUser和CreateUser修改为虚方法,GetUserAccountNo不需要改动,保留原有实现即可:

public class UserService : IUserService // 假设你实现的接口为IUserService
{
    public int GetUserAccountNo (string id)
    {
        if (GetUser(id)!=null )
        {
            return GetUser(id).AccountNo;
        }
        else
        {
            return CreateUser(id).AccountNo;
        }
    }

    // 新增virtual修饰
    public virtual User GetUser(string id)
    {
        //Get from  userAPi
        return new User();
    }

    // 新增virtual修饰
    public virtual User CreateUser(string id)
    {
        //Create from userAPi using  http request
        return new User();
    }
}

2. 编写单元测试代码

核心配置是创建UserService的Moq实例时开启CallBase = true,该配置的作用是:没有被手动Mock的方法会直接调用原类的真实实现,刚好符合你仅Mock两个方法、测试真实GetUserAccountNo逻辑的需求。
以下是完整的XUnit测试代码:

using Moq;
using Xunit;

public class UserServiceTests
{
    // 测试场景1:GetUser返回存在的用户,直接返回对应账号,不触发创建逻辑
    [Fact]
    public void GetUserAccountNo_WhenUserExists_ReturnsExistingAccountNo()
    {
        // 准备测试数据
        var testId = "test001";
        var expectedAccountNo = 12345;
        var existUser = new User { AccountNo = expectedAccountNo };

        // 创建UserService的部分Mock实例,开启CallBase
        var mockService = new Mock<UserService> { CallBase = true };
        // Mock GetUser方法,返回已存在的用户
        mockService.Setup(s => s.GetUser(testId)).Returns(existUser);

        // 调用真实的GetUserAccountNo方法
        var result = mockService.Object.GetUserAccountNo(testId);

        // 结果断言
        Assert.Equal(expectedAccountNo, result);
        // 验证CreateUser未被调用
        mockService.Verify(s => s.CreateUser(It.IsAny<string>()), Times.Never);
    }

    // 测试场景2:GetUser返回空,触发CreateUser返回新用户账号
    [Fact]
    public void GetUserAccountNo_WhenUserNotExists_ReturnsCreatedAccountNo()
    {
        // 准备测试数据
        var testId = "test002";
        var expectedAccountNo = 67890;
        var newUser = new User { AccountNo = expectedAccountNo };

        // 创建部分Mock实例
        var mockService = new Mock<UserService> { CallBase = true };
        // Mock GetUser返回空
        mockService.Setup(s => s.GetUser(testId)).Returns((User)null);
        // Mock CreateUser返回新创建的用户
        mockService.Setup(s => s.CreateUser(testId)).Returns(newUser);

        // 调用真实方法
        var result = mockService.Object.GetUserAccountNo(testId);

        // 结果断言
        Assert.Equal(expectedAccountNo, result);
        // 验证两个方法都被调用了一次
        mockService.Verify(s => s.GetUser(testId), Times.Once);
        mockService.Verify(s => s.CreateUser(testId), Times.Once);
    }
}

注意事项

  • 如果你的服务类有构造函数注入依赖,创建Mock实例时可以把依赖作为参数传入new Mock<UserService>(依赖1, 依赖2...)即可。
  • 不要用接口IUserService来创建Mock实例,接口的所有方法都是抽象的,开启CallBase也不会有默认实现,必须用具体的服务类作为Mock的泛型参数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 14:27:02