假设我有一个以这种方式实现的处理程序。没有存储库,逻辑按以下方式编写在处理程序内部。
既然我不应该嘲笑 EF,那么一个单元将如何测试它,因为它确实包含逻辑。
示例 - 我缩短了代码:
public record GetUsersQuery : IRequest<UserResult>
{
public List<string> UserType { get; set; }
}
internal sealed class GetUsersQueryHandler : IRequestHandler<GetUsersQuery, UserResult>
{
private readonly IDbContext context;
public GetUsersQueryHandler(IDbContext context)
{
this.context = context;
}
public async Task<UserResult> Handle(GetUsersQuery query, CancellationToken cancellationToken)
{
var baseQuery = context.AppUsers.AsQueryable();
baseQuery = baseQuery.Where(user => query.UserType.Contains(user.UserType));
/*
More Logic
*/
result = await baseQuery.ToListAsync(cancellationToken);
return MapToUserResult(result);
}
}
测试这一点很困难,但并非不可能。我可以想到两个选择。
如果您无法按原样更改代码,您可以考虑编写测试,以便代码也使用数据库。有些人会将此称为集成测试而不是单元测试,但如果做得正确,您仍然可以对代码进行自动测试覆盖。
关键是要做得正确,因为数据库是持久的。因此,您不能依赖垃圾收集在每次测试后进行清理。相反,您必须明确确保每个测试不会留下影响下一个测试的状态。
此外,您可能还需要在每次测试运行之前自动配置数据库。这些都不是不可能的,但另一方面也不是微不足道的,并且往往会使测试变慢。虽然我确实使用这种技术,但我倾向于考虑测试金字塔:不要过度。
有很多不同的方法可以做到这一点,即使在 .NET 上也是如此。我在我的书适合你头脑的代码中描述了一种这样的方式。
应用依赖倒置原则根据
依赖倒置原则,客户端代码应该依赖于抽象而不是实现细节。客户端代码应该定义抽象应该是什么样子,而不是通过抽象泄漏实现细节。
在这种情况下,看起来GetUsersQueryHandler
类真的需要一种方法来查询存储库以获取用户(?) 因此,定义一个执行此操作的接口,并将其注入到
GetUsersQueryHandler
而不是
IDbContext
中。将
IDbContext
、
Where
子句等移至新接口的实现中。