C#单元测试执行DealAccepted方法抛出NullReferenceException
问题现象
DealAccepted方法设计预期为仅从自定义数据结构中移除BoughtItems列表内的对应条目,在正式生产环境运行正常,但执行单元测试时固定抛出NullReferenceException空引用异常。
相关代码
DealAccepted方法实现
internal static bool DealAccepted(int amount) { foreach (var item in Manager.BoughtItems) { //Reduces Amount from Item and resets its LastTimeSold Property if (item.Amount > amount) { item.Amount -= amount; AddToTimeCheckListManager.AddToTimeCheckListBuy(item); if (item.Amount < Manager.MinSupplyAmount) { return false; } } //if we tried to order more or equal to the amount of current product else { amount -= item.Amount; TryRemoveItemFromDataStructures(item); } } DeleteAllLists(); return true; }
对应单元测试代码
[TestMethod] public void BuyBox_BuyEntireAmountOfSingleProduct_ProductDeleted() { Manager manager = new Manager(); Box box = new Box(6, 6); manager.AddItem(box, 10); ValueData data = new ValueData(box); Manager.BoughtItems.Add(data); manager.DealAccepted(5); }
故障原因
- 核心问题是静态依赖未初始化:
DealAccepted是静态方法,方法内部所有操作都依赖Manager类、AddToTimeCheckListManager类的静态成员,以及TryRemoveItemFromDataStructures、DeleteAllLists两个静态方法操作的静态数据结构。正式程序启动时会走完完整的静态初始化流程,所有用到的静态集合、配置都会提前赋值;但单元测试中仅实例化了一个Manager本地对象,没有执行任何静态初始化逻辑,上述静态对象均为null,调用时直接触发空引用。- 注意:C#中允许用实例对象调用静态方法,但编译后本质还是直接调用静态方法,完全不会用到实例化的
manager对象里的任何非静态成员,在实例上做的AddItem操作对静态方法里的逻辑没有任何作用。
- 注意:C#中允许用实例对象调用静态方法,但编译后本质还是直接调用静态方法,完全不会用到实例化的
- 测试用例本身构造的测试数据不完整:手动添加到
Manager.BoughtItems的ValueData对象仅传入了box参数,没有给Amount属性赋值,如果构造函数未对该属性设默认值,取值时也会触发空引用或逻辑错误。 - 代码存在隐藏bug:遍历
BoughtItems集合的过程中直接调用TryRemoveItemFromDataStructures修改原集合,就算空引用问题修复,后续也会抛出「集合已修改,枚举操作无法执行」的异常。
修复方案
- 为单元测试增加初始化流程:添加
[TestInitialize]标记的测试初始化方法,在里面完成所有相关静态类的成员初始化,对齐正式程序启动后的静态状态,不要仅实例化Manager对象就直接调用静态方法。 - 补全测试数据:构造
ValueData对象时给Amount属性赋正确的初始值,和业务逻辑预期一致。 - 修复遍历集合时修改原集合的问题:遍历过程中先把需要移除的条目存入临时列表,等遍历结束后再统一执行移除操作,或者改用倒序索引的for循环遍历集合。
内容的提问来源于stack exchange,提问作者Shalev Yegudayev
相关产品推荐
相关产品推荐

