Moq 是 .NET 中常用的 Mocking 框架,用于创建依赖的模拟对象,帮助开发者在单元测试中隔离和测试目标代码的行为。它允许对接口、抽象类和虚拟方法进行模拟。以下是 Moq 的核心功能及用法:
1. 安装 Moq
在项目中使用 NuGet 安装:
Install-Package Moq
或者使用 .NET CLI:
dotnet add package Moq
2. 创建 Mock 对象
Mock 对象是 Moq 的核心,用于模拟目标依赖。以下是创建 Mock<T>
对象的基本步骤:
var mockRepository = new Mock<IRepository>();
IRepository
是要模拟的接口或类。mockRepository
是 Mock 对象,通过它可以配置、调用和验证模拟行为。
3. 设置 Mock 行为
通过 Setup
方法配置被模拟方法的返回值或行为:
mockRepository.Setup(r => r.GetById(It.IsAny<int>())).Returns(new User { Id = 1, Name = "John" });
Setup
用于指定调用GetById
方法时的行为。It.IsAny<int>()
是 Moq 的匹配器,用于匹配任何整数参数。Returns
指定方法调用的返回值。
4. 验证方法调用
通过 Verify
方法验证模拟的方法是否按预期被调用:
mockRepository.Verify(r => r.GetById(1), Times.Once);
- 验证
GetById
方法是否被调用了一次,且参数为1
。 Times
提供多种验证调用次数的方法,如Times.Once
,Times.Never
,Times.AtLeastOnce
等。
5. 回调操作
使用 Callback
方法在模拟方法调用时执行额外操作:
mockRepository.Setup(r => r.Add(It.IsAny<User>()))
.Callback<User>(u => Console.WriteLine($"User {u.Name} added."));
Callback
可以记录调用参数或执行其他逻辑。
6. 抛出异常
模拟方法抛出异常:
mockRepository.Setup(r => r.GetById(It.IsAny<int>())).Throws(new Exception("Database error"));
7. 属性模拟
设置或验证属性:
mockRepository.SetupProperty(r => r.ConnectionString, "DefaultConnection");
Assert.Equal("DefaultConnection", mockRepository.Object.ConnectionString);
SetupProperty
设置属性的默认值。mockRepository.Object
是模拟的实际对象。
8. 模拟事件
模拟对象的事件:
mockRepository.Setup(r => r.SomethingHappened += It.IsAny<EventHandler>());
mockRepository.Object.SomethingHappened += (sender, args) => { /* Event handler logic */ };
9. 常见场景示例
模拟依赖并测试业务逻辑
假设有以下业务逻辑类:
public class UserService
{
private readonly IRepository _repository;
public UserService(IRepository repository)
{
_repository = repository;
}
public User GetUserById(int id)
{
return _repository.GetById(id);
}
}
测试 GetUserById
方法:
[Fact]
public void GetUserById_ReturnsUser()
{
// Arrange
var mockRepository = new Mock<IRepository>();
mockRepository.Setup(r => r.GetById(1)).Returns(new User { Id = 1, Name = "John" });
var userService = new UserService(mockRepository.Object);
// Act
var user = userService.GetUserById(1);
// Assert
Assert.NotNull(user);
Assert.Equal("John", user.Name);
// Verify
mockRepository.Verify(r => r.GetById(1), Times.Once);
}
10. 常用匹配器
Moq 提供许多匹配器,用于灵活匹配参数:
匹配器 | 说明 |
---|---|
It.IsAny<T>() | 匹配任何类型为 T 的参数 |
It.Is<T>(predicate) | 根据条件匹配参数,例如 It.Is<int>(x => x > 0) |
It.IsIn(range) | 参数在指定集合或范围中 |
It.IsNotNull() | 匹配非空参数 |
11. 高级功能
Mock 回调与 Returns 动态值
动态返回值和参数捕获:
mockRepository.Setup(r => r.Add(It.IsAny<User>()))
.Returns((User u) => u.Id + 1); // 返回基于参数的值
mockRepository.Setup(r => r.Delete(It.IsAny<int>()))
.Callback<int>(id => Console.WriteLine($"Deleted ID: {id}"));
Strict 模式
Mock 默认是宽松模式,未设置的方法返回默认值。通过严格模式可以强制验证所有调用:
var mockRepository = new Mock<IRepository>(MockBehavior.Strict);
Moq 是功能强大的工具,适合用于单元测试中隔离依赖和验证交互行为。通过合理使用,可以提升代码测试覆盖率和可维护性。