使用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
相关产品推荐
相关产品推荐

