如何Mock基类的异步泛型方法?Class2单元测试求助
解决方案
方法一:正确Mock受保护的异步泛型方法(使用Moq)
如果要Mock基类的PostAndDeserialize<T>方法,需要注意Moq对泛型保护方法的语法规则:泛型方法在反射中的名称格式为方法名``类型参数个数(比如单个泛型参数的方法名是PostAndDeserialize``1)。
以下是具体测试代码示例:
[TestClass] public class Class2Tests { [TestMethod] public async Task CreateSomething_ShouldReturnCorrectResponse() { // 准备模拟返回结果 var mockResult = new Result { Id = 123 }; // 创建Class2的Mock对象,允许执行非Mock的方法逻辑 var class2Mock = new Mock<Class2>(It.IsAny<Configuration>(), It.IsAny<CacheManager>()) { CallBase = true }; // Mock受保护的泛型异步方法 class2Mock.Protected() .Setup<Task<Result>>("PostAndDeserialize``1", It.IsAny<string>(), It.IsAny<object>()) .ReturnsAsync(mockResult); // 调用待测试方法 var result = await class2Mock.Object.CreateSomething(new Request()); // 断言结果符合预期 Assert.AreEqual(123, result.Id); } }
关键说明:
- 泛型方法名需写成
PostAndDeserialize``1(两个反引号加类型参数数量,这里T是单个类型参数) - 通过
Setup<Task<Result>>指定方法返回的泛型类型 CallBase = true确保Class2的CreateSomething方法能正常执行自身逻辑并调用Mock后的基类方法
方法二:重构代码优化测试体验(更推荐)
原代码的核心问题是Class1直接在构造函数中实例化Client,未采用依赖注入,导致测试时不得不Mock受保护方法。重构代码让依赖可注入,会让测试更简单直观:
第一步:重构Class1,注入IClient依赖
public class Class1 { private readonly IClient _client; // 改为注入IClient,而非自行实例化 public Class1(IClient client) { _client = client ?? throw new ArgumentNullException(nameof(client)); } protected async Task<T> PostAndDeserialize<T>(string param1, object param2) { var result = await _client.PostSomething(param1, param2); return JsonConvert.DeserializeObject<T>(await result.Content.ReadAsStringAsync()); } } public class Class2 : Class1 { // 传递IClient实例给基类 public Class2(IClient client) : base(client) { } public async Task<Response> CreateSomething(Request request) { // 示例简化,假设此处对request做必要处理 var test = request; var result = await PostAndDeserialize<Result>("test", test); return new Response { Id = result.Id }; } }
第二步:测试Class2,直接Mock IClient
[TestClass] public class Class2Tests { [TestMethod] public async Task CreateSomething_ShouldReturnCorrectResponse() { // 准备模拟数据 var mockResult = new Result { Id = 123 }; var jsonContent = new StringContent(JsonConvert.SerializeObject(mockResult), Encoding.UTF8, "application/json"); var mockHttpResponse = new HttpResponseMessage(HttpStatusCode.OK) { Content = jsonContent }; // Mock IClient的PostSomething方法 var clientMock = new Mock<IClient>(); clientMock.Setup(c => c.PostSomething(It.IsAny<string>(), It.IsAny<object>())) .ReturnsAsync(mockHttpResponse); // 实例化Class2并传入Mock的IClient var class2 = new Class2(clientMock.Object); // 调用待测试方法 var response = await class2.CreateSomething(new Request()); // 断言结果符合预期 Assert.AreEqual(123, response.Id); // 验证IClient的方法是否按预期被调用 clientMock.Verify(c => c.PostSomething("test", It.IsAny<Request>()), Times.Once); } }
这种方式无需Mock受保护方法,直接针对底层依赖IClient做Mock,更符合单元测试的单一职责原则,代码也更易维护。
内容的提问来源于stack exchange,提问作者Hubert Brzostowski
相关产品推荐
相关产品推荐

