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

使用XUnit与Moq实现更新操作失败,请求技术协助

问题排查与解决方案

咱们来一步步拆解你遇到的核心问题:调用SetPortfolioCompanyStatus返回null以及更新操作未生效,根源都出在Moq的匹配逻辑和测试代码的细节上。

1. 为什么返回null?Moq匹配失败了!

你在测试里给UpdatePortfolioCompany做的Setup是这样的:

mockUserRepository.Setup(m => m.UpdatePortfolioCompany(moqData.portfolioCompanies[0], It.IsAny<CancellationToken>()))
                  .ReturnsAsync(moqData.portfolioCompanies[0]);

Moq默认是按对象引用匹配的——只有当传入的参数和moqData.portfolioCompanies[0]是同一个引用时,才会返回你设置的结果。但实际调用控制器时,你传入的是一个新建的PortfolioCompany对象:

PortfolioCompany product = new PortfolioCompany() {
 Id = 1, Name = "Test Private Company 1",//change data
 DealCode = "PRIV1", IsActive = false, IsPrivate = true
 };

虽然新对象的Id和Mock数据里的第一个对象一致,但它是全新的引用,Moq认不出来,所以会返回默认值null。

修复方案:修改Moq的匹配规则

把Setup改成按Id匹配,而不是按引用,同时模拟真实的更新逻辑(同步属性到Mock集合):

mockUserRepository.Setup(m => m.UpdatePortfolioCompany(
    It.Is<PortfolioCompany>(pc => pc.Id == 1), // 匹配任意Id为1的PortfolioCompany对象
    It.IsAny<CancellationToken>()))
                  .ReturnsAsync((PortfolioCompany pc, CancellationToken ct) => {
                      // 同步传入对象的属性到Mock集合中的对应项
                      var existing = moqData.portfolioCompanies.First(x => x.Id == pc.Id);
                      existing.IsActive = pc.IsActive;
                      existing.Name = pc.Name;
                      existing.DealCode = pc.DealCode;
                      existing.IsPrivate = pc.IsPrivate;
                      // 返回更新后的对象
                      return existing;
                  });

2. 更新操作未生效?Mock数据没同步!

之前的Setup只返回了单个对象,没有修改Mock集合里的原始数据,所以调用GetAllPortfolioCompanies后看不到变化。上面的修复方案里已经包含了同步Mock集合数据的逻辑,这样查询时就能看到更新后的结果了。

3. 额外的小问题:测试方法用async Task代替async void

你的测试方法是async void,这在XUnit里会导致测试无法正确等待异步操作完成,可能出现不可预测的结果。改成async Task:

[Fact] 
public async Task SetPortfolioCompanyStatusAsync() { // 把void改成Task
    // ... 剩下的测试代码不变
}

4. 控制器代码的小优化

控制器里重复调用了两次UpdatePortfolioCompany,其实没必要,调用一次即可:

public async Task<ActionResult<PortfolioCompany>> SetPortfolioCompanyStatus([FromBody] PortfolioCompany portfolioCompany, CancellationToken cancellationToken = default) {
    if (portfolioCompany == null) throw new ArgumentNullException(nameof(portfolioCompany));
    if (portfolioCompany.Id <= 0) throw new ArgumentOutOfRangeException(nameof(portfolioCompany.Id));
    
    var updatedCompany = await _portfolioCompanyManagement.UpdatePortfolioCompany(portfolioCompany, cancellationToken);
    return updatedCompany;
}

修复后的测试代码示例

整合所有修改后的测试代码如下:

[Fact] 
public async Task SetPortfolioCompanyStatusAsync() {
    int targetId = 1;
    var mockPortfolioRepo = new Mock<IPortfolioCompanyManagement>();
    var mockUserRepo = new Mock<IUserManagement>();
    
    // Setup 获取所有数据的方法
    mockPortfolioRepo.Setup(c => c.GetPortfolioCompanies(It.IsAny<CancellationToken>()))
                      .Returns(Task.FromResult(moqData.portfolioCompanies));
    
    // Setup 更新方法:匹配指定Id,同步属性到Mock集合
    mockPortfolioRepo.Setup(m => m.UpdatePortfolioCompany(
        It.Is<PortfolioCompany>(pc => pc.Id == targetId), 
        It.IsAny<CancellationToken>()))
                      .ReturnsAsync((PortfolioCompany pc, CancellationToken ct) => {
                          var existing = moqData.portfolioCompanies.First(x => x.Id == pc.Id);
                          existing.IsActive = pc.IsActive;
                          existing.Name = pc.Name;
                          existing.DealCode = pc.DealCode;
                          existing.IsPrivate = pc.IsPrivate;
                          return existing;
                      });
    
    // 构造要更新的对象
    PortfolioCompany updateRequest = new PortfolioCompany() {
        Id = targetId, 
        Name = "Test Private Company 1",
        DealCode = "PRIV1", 
        IsActive = false, 
        IsPrivate = true
    };
    
    var controller = new UsersController(mockUserRepo.Object, mockPortfolioRepo.Object);
    var updateResult = await controller.SetPortfolioCompanyStatus(updateRequest, CancellationToken.None);
    
    // 验证返回的更新对象不为空且属性正确
    Assert.NotNull(updateResult.Value);
    Assert.False(updateResult.Value.IsActive);
    
    // 验证集合中的数据也已更新
    var getAllResult = await controller.GetAllPortfolioCompanies(CancellationToken.None);
    var updatedItem = getAllResult.Value.First(x => x.Id == targetId);
    Assert.False(updatedItem.IsActive);
}

这样修改后,你的测试应该就能正确返回更新后的对象,并且集合里的数据也会同步更新啦。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:23:09