我正在尝试模拟一个具有在抽象类中实现的通用返回类型的方法。
抽象类代码:
public abstract class AbstractRepository<T> where T : class
{
protected virtual async Task<T> GetById(int id)
{
// some code
}
}
实现类:
public class UserRepository : AbstractRepository<User>
{
public Task<User> GetUserById(int id)
{
return base.GetById(id);
}
}
测试方法:
[Theory]
public async Task GetUserById_UserExists_ReturnsUser(User user)
{
Mock<UserRepository> mockRealSut = new(MockBehavior.Strict, new object[] {
})
{
CallBase = true
};
//Setup Mock
//Act
User result = await mockRealSut.Object.GetUserById(1);
}
我无法成功设置模拟,因为始终调用实际的抽象方法。
我尝试了两种方法:
方法一:
_ = mockRealSut.Protected().Setup<Task<User>>("GetById", ItExpr.IsAny<int>()).ReturnsAsync(user);
方法2:
public interface IGetByIdMethodMock<T>
{
Task<T> GetById(int id);
}
_ = mockRealSut.Protected().As<IGetByIdMethodMock<User>>().Setup(m => m.GetById(It.IsAny<int>())).ReturnsAsync(user);
stackoverflow 上的这篇文章 here 似乎表明方法 2) 应该有效,但它特定于泛型参数,而不是泛型返回类型。
我查看了 Moq 文档,但找不到解决方案。
我错过了什么吗? 我认为这应该可以通过 Moq 实现,但是缺少一些东西,我找不到问题。
这种类型的 CRUD 方法非常常见,所以如果 Moq 不涵盖这种类型的代码那就很奇怪了。
问题是
GetUserById
方法调用了base.GetById
:
return base.GetById(id);
这意味着它显式调用
AbstractRepository<User>.GetById
,而不是 UserRepository.GetById
。
当您要求 Moq 覆盖
UserRepository.GetById
时,您会这样做,但您不会覆盖 AbstractRepository<User>.GetById
。
将
GetUserById
方法更改为:
public Task<User> GetUserById(int id)
{
return GetById(id);
}
然后这样的测试就可以了:
[Theory]
[InlineData(1)]
[InlineData(2)]
public async Task GetUserById(int id)
{
var expected = new User();
var repo = new Mock<UserRepository>();
repo.Protected()
.Setup<Task<User>>("GetById", ItExpr.Is<int>(i => i == id))
.ReturnsAsync(expected);
var actual = await repo.Object.GetUserById(id);
Assert.Equal(expected, actual);
}