You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 14:50:21