如何在NUnit模拟断言中获取被调用函数的传入参数数量
验证ProcessData调用BulkUpdateData时的参数数量(.NET 6 + NUnit)
要验证ProcessData最终传入BulkUpdateData的有效对象数量,核心思路是拦截BulkUpdateData的调用并捕获传入的参数,这里推荐使用Moq框架配合NUnit实现,以下是两种可行方案:
方案1:修改原类,让BulkUpdateData成为虚方法
如果允许修改目标类的代码,只需将BulkUpdateData标记为virtual,即可用Moq拦截调用:
步骤1:调整目标类
public class DataProcessor { public void ProcessData(IEnumerable<EventData> events) { // 原逻辑保持不变 List<TestClass> testClasses = new(); events.ToList().ForEach(msg => { var testClass = JsonConvert.DeserializeObject<TestClass>(msg.EventBody.ToString()); if(testClass != null) { testClasses.Add(testClass); } }); if(testClasses.Count > 0) { BulkUpdateData(testClasses); } } // 标记为virtual,允许Moq拦截 public virtual void BulkUpdateData(List<TestClass> testClasses) { // 原业务逻辑 } }
步骤2:编写NUnit测试用例
using NUnit.Framework; using Moq; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; [TestFixture] public class DataProcessorTests { [Test] public void ProcessData_10EventsWith2Invalid_Passes8ValidItemsToBulkUpdate() { // Arrange // 创建DataProcessor的Mock实例,CallBase=true确保ProcessData执行真实逻辑 var mockProcessor = new Mock<DataProcessor> { CallBase = true }; List<TestClass> receivedItems = null; // 拦截BulkUpdateData调用,保存传入的参数 mockProcessor.Setup(p => p.BulkUpdateData(It.IsAny<List<TestClass>>())) .Callback<List<TestClass>>(items => receivedItems = items); // 构造测试数据:8个有效,2个无效 var testEvents = new List<EventData>(); // 添加8个可正常反序列化的EventData for (int i = 0; i < 8; i++) { testEvents.Add(new EventData { EventBody = JsonConvert.SerializeObject(new TestClass { Name = $"Test{i}", Id = i }) }); } // 添加2个无效数据(格式错误或类型不匹配) testEvents.Add(new EventData { EventBody = "{invalid-json}" }); testEvents.Add(new EventData { EventBody = "{\"Name\": \"Invalid\", \"Id\": \"not-an-int\"}" }); // Act mockProcessor.Object.ProcessData(testEvents); // Assert Assert.Multiple(() => { Assert.That(receivedItems, Is.Not.Null, "BulkUpdateData未被调用"); Assert.That(receivedItems.Count, Is.EqualTo(8), "传入BulkUpdateData的有效数据数量不符"); }); } } // 补充EventData的定义(根据实际情况调整) public class EventData { public object EventBody { get; set; } }
方案2:依赖注入解耦(推荐)
如果遵循SOLID原则,建议将BulkUpdateData的逻辑提取为独立服务,通过依赖注入传入DataProcessor,这种方式无需修改原业务方法的访问修饰符,测试灵活性更高:
步骤1:提取接口与实现类
// 定义数据更新接口 public interface IDataUpdater { void BulkUpdateData(List<TestClass> testClasses); } // 实现业务逻辑 public class DataUpdater : IDataUpdater { public void BulkUpdateData(List<TestClass> testClasses) { // 原BulkUpdateData的业务逻辑 } } // 修改DataProcessor,注入IDataUpdater public class DataProcessor { private readonly IDataUpdater _dataUpdater; public DataProcessor(IDataUpdater dataUpdater) { _dataUpdater = dataUpdater; } public void ProcessData(IEnumerable<EventData> events) { List<TestClass> testClasses = new(); events.ToList().ForEach(msg => { var testClass = JsonConvert.DeserializeObject<TestClass>(msg.EventBody.ToString()); if(testClass != null) { testClasses.Add(testClass); } }); if(testClasses.Count > 0) { _dataUpdater.BulkUpdateData(testClasses); } } }
步骤2:编写NUnit测试用例
using NUnit.Framework; using Moq; using Newtonsoft.Json; using System.Collections.Generic; [TestFixture] public class DataProcessorTests { [Test] public void ProcessData_10EventsWith2Invalid_Passes8ValidItemsToBulkUpdate() { // Arrange var mockUpdater = new Mock<IDataUpdater>(); List<TestClass> receivedItems = null; // 拦截IDataUpdater的BulkUpdateData调用,捕获参数 mockUpdater.Setup(u => u.BulkUpdateData(It.IsAny<List<TestClass>>())) .Callback<List<TestClass>>(items => receivedItems = items); // 注入mock服务到DataProcessor var processor = new DataProcessor(mockUpdater.Object); // 构造测试数据(同方案1) var testEvents = new List<EventData>(); for (int i = 0; i < 8; i++) { testEvents.Add(new EventData { EventBody = JsonConvert.SerializeObject(new TestClass { Name = $"Test{i}", Id = i }) }); } testEvents.Add(new EventData { EventBody = "{invalid-json}" }); testEvents.Add(new EventData { EventBody = "{\"Name\": \"Invalid\", \"Id\": \"not-an-int\"}" }); // Act processor.ProcessData(testEvents); // Assert Assert.Multiple(() => { Assert.That(receivedItems, Is.Not.Null); Assert.That(receivedItems.Count, Is.EqualTo(8)); // 额外验证BulkUpdateData仅被调用一次 mockUpdater.Verify(u => u.BulkUpdateData(It.Is<List<TestClass>>(list => list.Count == 8)), Times.Once); }); } }
关键说明
- 两种方案都通过Moq的
Callback方法捕获BulkUpdateData的传入参数,从而验证有效数据的数量。 - 方案2的依赖注入方式更利于代码解耦和后续维护,是长期项目的推荐做法。
内容的提问来源于stack exchange,提问作者cgsabari
相关产品推荐
相关产品推荐

