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

C#单元测试Task.WhenAll异步调用抛出NullReferenceException

问题原因

两个核心问题导致空引用:

  1. Mock配置的调用次数不匹配:你用的Rhino Mocks默认Expect配置仅对1次调用生效,重构前你只调用1次GetMeSomething()所以能正常跑;重构后someList.Select会迭代集合里的所有元素,集合有多少个元素就会触发多少次GetMeSomething()调用,从第二次调用开始,mock没有对应配置规则,会直接返回Task<WhateverType>类型的默认值null,代码执行await null时就会抛出空引用异常。
  2. 测试代码存在书写错误:你声明的mock实例变量是小写开头的provider,但配置Expect时写成了大写开头的Provider,同时你贴的代码里大括号位置错误,初始化业务类、赋值依赖、调用被测方法的代码被写到了测试方法的作用域外,就算配置了mock规则也不会生效到你注入到业务类的实例上。

另外业务代码里的LINQ Select是延迟执行的,直接传给Task.WhenAll虽然能跑,但如果后续重复枚举someTask会触发重复执行,存在隐性bug。

修复方案
  • 修正测试代码的书写问题:把所有测试逻辑移到测试方法的作用域内,配置mock规则时使用正确的小写provider变量名。
  • 给mock规则添加重复调用配置,允许任意次调用都返回预设结果:
provider
    .Expect(x => x.GetMeSomething())
    .Return(Task.FromResult(someExpectedResult))
    .Repeat.Any();
  • 优化业务代码的任务集合生成逻辑,显式调用ToList()立即执行LINQ迭代,避免延迟执行带来的重复执行问题,同时补全缺失的语法闭合标记:
public async Task<List<SomeType>> GetAnotherThing()
{
    var someTasks = someList.Select(async anotherList => 
    {
        var someAsyncCall = await this.Provider.GetMeSomething();
        // 使用返回结果的后续代码
    }).ToList();

    await Task.WhenAll(someTasks);

    // 其他业务逻辑代码
}

补充:异步场景下如果mock方法返回Task类型但没有配置规则,mock会返回默认值null,await null会直接抛出空引用,这是mock配置缺失的典型表现,和业务逻辑本身无关。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:27:23