C#中如何Mock Func<Task<泛型类型>>并模拟仓储而非仅模拟代理?
问题描述
我实现了一个泛型类GenericProxy : IProxy,包含如下方法:
public async Task<T> SomeMethod<T>(Func<Task<T>> action, bool ignore) { if (ignore) { return await action(); } return await proxy<T>.ExecuteAsync(_ => action()); }
编写了如下调用代码:
public async Task<string> StoreBook(string bookName) { var findBookResult = await proxy.SomeMethod<string>(async () => await BorrowBookProcess(bookName), false); return findBookResult; } public async Task<string> BorrowBookProcess (string bookName) { // do something return await bookRepository.findbook(bookName); }
单元测试中,以下测试方法可正常运行:
[Fact] public async void SomeMethod_return_AsExpected() { Mock<IProxy> mockPoxy = new Mock<IProxy>(); string assertResult = "Finded"; mockPoxy.Setup( x => x.SomeMethod( It.IsAny<Func<Task<string>>>(), false)) .ReturnsAsync(assertResult) .Verifiable(); var getReturn = sut.StoreBook(assertResult); assert.equal(getReturn, assertResult); }
但出于某些原因,我想模拟bookRepository并返回值,而非仅模拟IProxy,请问该如何实现?
解决方案
要直接模拟bookRepository而非IProxy,核心是让被测试对象依赖真实的GenericProxy实例,同时注入Mock的bookRepository,具体步骤如下:
第一步:确保
bookRepository是可注入的依赖
包含StoreBook和BorrowBookProcess的类,需要通过构造函数注入bookRepository对应的抽象接口(如果还没抽象接口,先给bookRepository定义一个IBookRepository接口),示例代码:public class BookService { private readonly IProxy _proxy; private readonly IBookRepository _bookRepository; // 构造函数注入依赖 public BookService(IProxy proxy, IBookRepository bookRepository) { _proxy = proxy; _bookRepository = bookRepository; } public async Task<string> StoreBook(string bookName) { var findBookResult = await _proxy.SomeMethod<string>(async () => await BorrowBookProcess(bookName), false); return findBookResult; } public async Task<string> BorrowBookProcess(string bookName) { // do something return await _bookRepository.findbook(bookName); } }第二步:编写测试代码,Mock
IBookRepository并使用真实GenericProxy
测试时不再MockIProxy,而是实例化真实的GenericProxy,同时MockIBookRepository并预设返回值:[Fact] public async Task StoreBook_ShouldReturnBookFromRepository() { // 测试数据准备 string testBookName = "Test Book"; string expectedResult = "Finded Test Book"; // Mock 书籍仓储 var mockBookRepo = new Mock<IBookRepository>(); mockBookRepo.Setup(repo => repo.findbook(testBookName)) .ReturnsAsync(expectedResult); // 实例化真实代理和被测试对象 var realProxy = new GenericProxy(); // 若GenericProxy有依赖,需同步注入对应Mock var sut = new BookService(realProxy, mockBookRepo.Object); // 执行测试方法 var actualResult = await sut.StoreBook(testBookName); // 断言结果匹配 Assert.Equal(expectedResult, actualResult); // 验证仓储方法被正确调用 mockBookRepo.Verify(repo => repo.findbook(testBookName), Times.Once); }注意事项
- 原测试用
async void是不良实践,建议改为async Task,避免异步测试的潜在问题; - 如果
GenericProxy本身有其他依赖,也要为这些依赖注入Mock,保证测试环境的隔离性; - 需确保
GenericProxy的SomeMethod在ignore=false时,能正确执行传入的action逻辑,这样Mock的bookRepository才会被调用到。
- 原测试用
内容的提问来源于stack exchange,提问作者Carrie Kaski
相关产品推荐
相关产品推荐

