如何让NUnit测试在SaveOrderAllocation计算逻辑变更时自动失败?
解决单元测试无法检测SaveOrderAllocation逻辑变更的问题
问题背景
Presenter层的SaveOrderAllocation方法原本逻辑为:非无限分配模式下,计算预分配量的90%与预订单需求量(OpenQuantity+ReleasedQuantity+ShippedQuantity)的最大值,将该值赋值给内部创建的orderAllocationInput.PreorderAllocation字段后,调用ItemDao.SavePreorderAllocation。但当方法逻辑被修改为仅取90%预分配量时,现有NUnit单元测试未能自动失败,需在不修改原方法体的前提下调整测试断言,使其能检测到这类逻辑变更。
现有代码分析
原SaveOrderAllocation方法核心逻辑
public SaveActionResultWithCustomizedErrors<int> SaveOrderAllocation(OrderAllocation orderAllocation, FillOrderOutput fillOrderOutput) { var saveResult = new SaveActionResultWithCustomizedErrors<int>(); int? maxOfNinetyPercentAllocationQty = null; var title = new orderAllocationInput() {Ean = orderAllocation.Ean}; try { if (!orderAllocation.IsUnlimitedAllocation) { if (orderAllocation.PreorderAllocationQty != null & orderAllocation.PreorderAllocationQty.HasValue) { var preDemandQty = (fillOrderOutput.OpenQuantity + fillOrderOutput.ReleasedQuantity + fillOrderOutput.ShippedQuantity); maxOfNinetyPercentAllocationQty = Math.Max(.9 * orderAllocation.PreorderAllocationQty.Value, preDemandQty).RoundToInt(); } if (maxOfNinetyPercentAllocationQty != null) title.PreorderAllocation = maxOfNinetyPercentAllocationQty.Value; } ItemDao.SavePreorderAllocation(orderAllocation, fillOrderOutput); saveResult.SuccessfulItems.Add(orderAllocation.SystemId, orderAllocation.SystemId); } // 省略异常处理等代码 }
现有单元测试代码
[Test] public void SaveOrderAllocation_With_OrderAllocationToMaxOf90Percent_Or_PreDemandQtyTest() { // Arrange var orderAllocation = new OrderAllocation { Ean = "EAN123", Sysid = 1, PreorderAllocationQty = 100, IsUnlimitedAllocation = false }; var fillOrderOutput = new FillOrderOutput { OpenQuantity = 80, ReleasedQuantity = 30, ShippedQuantity = 20 }; var preDemandQty = fillOrderOutput.OpenQuantity + fillOrderOutput.ReleasedQuantity + fillOrderOutput.ShippedQuantity; var expected = Math.Max(.9 * orderAllocation.PreorderAllocationQty.Value, preDemandQty).RoundToInt(); // Act var result = Testee.SaveOrderAllocation(orderAllocation, fillOrderOutput); // Assert _itemDaoMock.Verify(x => x.SavePreorderAllocation(orderAllocation, fillOrderOutput), Times.Once()); Assert.IsTrue(result.Success); Assert.That(result.FailedItems.Count, Is.EqualTo(0)); Assert.That(result.SuccessfulItems.Count, Is.EqualTo(1)); Assert.AreEqual(expected, preDemandQty); // 这行断言完全无效 }
测试失效原因
现有测试存在两个关键问题:
- 无效断言:
Assert.AreEqual(expected, preDemandQty)中,expected是按原逻辑计算的130,而preDemandQty本身就是测试代码中硬算的130,与方法内部的计算逻辑完全无关,无论方法逻辑怎么改,这行断言永远成立。 - 未验证核心逻辑:测试只验证了
SavePreorderAllocation的调用次数,却没验证方法核心计算结果(即title.PreorderAllocation的值)是否被正确传递给了DAO层。
解决方案
调整测试逻辑,通过Mock验证SavePreorderAllocation被调用时,传入的参数中包含符合原逻辑的预期值。由于原方法中title是内部创建的,需通过参数匹配器验证其属性:
调整后的单元测试代码
[Test] public void SaveOrderAllocation_With_OrderAllocationToMaxOf90Percent_Or_PreDemandQtyTest() { // Arrange var orderAllocation = new OrderAllocation { Ean = "EAN123", Sysid = 1, PreorderAllocationQty = 100, IsUnlimitedAllocation = false }; var fillOrderOutput = new FillOrderOutput { OpenQuantity = 80, ReleasedQuantity = 30, ShippedQuantity = 20 }; var preDemandQty = fillOrderOutput.OpenQuantity + fillOrderOutput.ReleasedQuantity + fillOrderOutput.ShippedQuantity; var expectedPreorderAllocation = Math.Max(.9 * orderAllocation.PreorderAllocationQty.Value, preDemandQty).RoundToInt(); // Act var result = Testee.SaveOrderAllocation(orderAllocation, fillOrderOutput); // Assert // 验证DAO方法被调用时,传入的orderAllocationInput(即title)的PreorderAllocation等于预期值 // 若SavePreorderAllocation实际接收OrderAllocation作为第一个参数,需调整匹配器验证对应属性 _itemDaoMock.Verify(x => x.SavePreorderAllocation( It.Is<orderAllocationInput>(input => input.PreorderAllocation == expectedPreorderAllocation), fillOrderOutput), Times.Once()); // 保留基础结果断言 Assert.IsTrue(result.Success); Assert.That(result.FailedItems.Count, Is.EqualTo(0)); Assert.That(result.SuccessfulItems.Count, Is.EqualTo(1)); }
关键调整说明
- 移除了无效的
Assert.AreEqual(expected, preDemandQty)断言。 - 修改
_itemDaoMock.Verify,使用It.Is<T>参数匹配器,验证传入SavePreorderAllocation的orderAllocationInput对象的PreorderAllocation字段值是否符合原逻辑的预期值。 - 如果原方法中
SavePreorderAllocation的第一个参数是OrderAllocation,且方法内部将title.PreorderAllocation赋值给了orderAllocation的对应属性,则将匹配器改为验证OrderAllocation的该属性值。
这样修改后,当SaveOrderAllocation的计算逻辑被改为仅取90%预分配量时,expectedPreorderAllocation会是130,而实际传入DAO的PreorderAllocation会是90,Mock验证会失败,测试就能正确检测到逻辑变更。
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

