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

C#使用Xunit+Mock测试控制器时AddQuestionToBank无返回值问题求助

问题原因

你的Mock配置未生效本质是Moq参数匹配规则的问题,同时还有几个潜在的配置遗漏:

  1. Moq默认对引用类型参数按引用相等做匹配,你在初始化Mock时用了一个CampaignQuestionsViewModel实例做Setup,但测试方法内又重新new了一个全新的同类型实例传入控制器,两个实例引用地址不同,导致Mock匹配失败,直接返回默认值null。
  2. 异步方法的返回配置可以用更简洁的ReturnsAsync,避免手动构造Task.FromResult出错。
  3. 未配置IStringLocalizer的Mock返回值,后续代码执行到string.Format时会因为拿到null值抛异常。

修正方案

1. 修正Service的Mock配置

把两个Service的Setup参数从固定实例改为类型匹配,同时简化异步返回写法:

// 匹配任意CampaignQuestionsViewModel实例和任意int类型的userId
questionServiceMock.Setup(x => x.AddQuestionToBank(
    It.IsAny<CampaignQuestionsViewModel>(), 
    It.IsAny<int>()))
.ReturnsAsync(question1); // 等价于Returns(Task.FromResult(question1))

// 匹配任意CampaignQuestionsViewModel、任意userId、任意campaignId
campaignQuestionServiceMock.Setup(x => x.AddQuestionToCampaign(
    It.IsAny<CampaignQuestionsViewModel>(), 
    It.IsAny<int>(), 
    It.IsAny<int>()))
.ReturnsAsync(campaignQuestion);

如果你需要严格匹配参数内容而不是匹配任意实例,可以给CampaignQuestionsViewModel重写Equals方法,或者在It.Is里加判断条件:

// 示例:只匹配BaseTitle为Title2的ViewModel
It.Is<CampaignQuestionsViewModel>(q => q.BaseTitle == "Title2")

2. 补充StringLocalizer的Mock配置

避免字符串格式化时抛空引用异常:

// 按你实际的错误字符串格式配置即可,这里示例错误模板是"错误码{0}:{1}"
stringLocalizer.Setup(_ => _[Constants.ErrorCodeString]).Returns("ErrorCode:{0} Message:{1}");
stringLocalizer.Setup(_ => _[Helper.ToEnumString(ErroCodes.InternalServerError)]).Returns("服务内部错误");
stringLocalizer.Setup(_ => _[Helper.ToEnumString(ErroCodes.NoQuestionsToAdd)]).Returns("没有可添加的问题");
stringLocalizer.Setup(_ => _[Helper.ToEnumString(ErroCodes.SpecifiedCamIdIsNotValid)]).Returns("指定的活动ID无效");

3. 优化测试方法逻辑

你当前的测试用例是正常调用场景(campaignId=1,question不为空),可以直接针对性断言,不需要多分支判断:

[Fact]
public async Task AddQuestionToCampaign_ValidInput_ReturnsOk()
{
    // 直接复用初始化Mock时创建的campaignQuestionsViewModel即可,不需要重新new
    var actionResult = await questionsController.AddQuestionToCampaign(campaignQuestionsViewModel, 1);
    var result = actionResult as ObjectResult;
    
    Assert.NotNull(result);
    Assert.Equal(200, result.StatusCode);
    Assert.NotNull(result.Value as CampaignQuestions);
}

如果需要覆盖错误分支,可以单独写对应的测试用例,比如传空的question、传campaignId=0等场景。

内容的提问来源于stack exchange,提问作者Tharindu Sandaruwan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 22:45:03