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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 18:40:32