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

如何对继承自ModuleBase的派生类进行单元测试并模拟其Context属性

Discord.Net ModuleBase 单元测试Context劫持方案

方案说明

你当前重写Context为自动属性的方案是可行的,但还有侵入性更低、覆盖更完整的实现方式,无需修改业务模块的默认Context定义:
ModuleBase<TContext>的Context本身就是虚属性,可直接赋值或通过Mock框架注入伪造实例,SocketCommandContext及其成员(如User、Guild)均可通过Mock框架构造伪造对象,同时还可以验证基类ReplyAsync等方法的调用行为。

完整测试实现示例

以Moq框架为例,测试代码如下:

private readonly Mock<IRepository> _repositoryMock = new(MockBehavior.Strict);
private readonly Mock<SocketCommandContext> _contextMock = new(MockBehavior.Strict);
private readonly Mock<SocketGuildUser> _mockUser = new(MockBehavior.Strict);
private const ulong TestUserId = 123456789UL;

[TestMethod]
public async Task Should_Reply_With_Repository_Value_When_No_TargetUser_Provided()
{
    // 配置伪造用户信息
    _mockUser.Setup(u => u.Id).Returns(TestUserId);
    
    // 配置伪造执行上下文
    _contextMock.Setup(c => c.User).Returns(_mockUser.Object);
    
    // 配置仓储预期行为
    const string testResult = "测试返回内容";
    _repositoryMock
        .Setup(pr => pr.GetAsync(TestUserId))
        .ReturnsAsync(testResult)
        .Verifiable();

    // Mock业务模块,CallBase保证业务方法走真实逻辑
    var moduleMock = new Mock<SampleModule>(_repositoryMock.Object) 
    { 
        CallBase = true 
    };
    // 注入伪造上下文
    moduleMock.Object.Context = _contextMock.Object;
    // 配置ReplyAsync调用验证规则
    moduleMock.Setup(m => m.ReplyAsync(testResult, null, false, false, null, null, null, null))
        .Verifiable();

    // 执行测试方法
    await moduleMock.Object.RunAsync();

    // 验证所有预期行为均被触发
    _repositoryMock.Verify();
    moduleMock.Verify();
}

额外说明

  • 你提供的业务代码中构造函数存在笔误:参数名_repository和类字段重名,赋值逻辑应改为this._repository = repository
  • 如果需要测试指定targetUser的场景,直接传入伪造的SocketGuildUser实例即可,无需配置Context的User属性
  • 若不想使用Mock框架,也可以手动构造DiscordSocketClient、SocketGuild等真实实例组装SocketCommandContext,但配置成本更高

内容的提问来源于stack exchange,提问作者Józef Podlecki

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 14:06:02