C#单元测试Task.WhenAll异步调用抛出NullReferenceException
问题原因
两个核心问题导致空引用:
- Mock配置的调用次数不匹配:你用的Rhino Mocks默认
Expect配置仅对1次调用生效,重构前你只调用1次GetMeSomething()所以能正常跑;重构后someList.Select会迭代集合里的所有元素,集合有多少个元素就会触发多少次GetMeSomething()调用,从第二次调用开始,mock没有对应配置规则,会直接返回Task<WhateverType>类型的默认值null,代码执行await null时就会抛出空引用异常。 - 测试代码存在书写错误:你声明的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
相关产品推荐
相关产品推荐

