C#使用Xunit+Mock测试控制器时AddQuestionToBank无返回值问题求助
问题原因
你的Mock配置未生效本质是Moq参数匹配规则的问题,同时还有几个潜在的配置遗漏:
- Moq默认对引用类型参数按引用相等做匹配,你在初始化Mock时用了一个
CampaignQuestionsViewModel实例做Setup,但测试方法内又重新new了一个全新的同类型实例传入控制器,两个实例引用地址不同,导致Mock匹配失败,直接返回默认值null。 - 异步方法的返回配置可以用更简洁的
ReturnsAsync,避免手动构造Task.FromResult出错。 - 未配置
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
相关产品推荐
相关产品推荐

