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

如何优化验证无线设备方法调用情况的自定义函数?

优化方法调用验证函数的实现方案

我需要实现一个可接收方法名称,并验证该方法是否被调用的功能,目前已编写如下自定义函数,现寻求更优的实现方案。

更新说明:根据反馈已对该函数进行更新,欢迎提出进一步优化意见。

原实现代码:

private void Then_WirelessDeviceInvokedMethod(
    ushort unitId, 
    string methodName = null, 
    int expectedCallCount = 1, 
    IReadOnlyList<object> expectedArgs = null)
{
    if (expectedArgs == null)
    {
        expectedArgs = new List<object>();
    }
    var wdm = GetWirelessDeviceMock(unitId);
    Assert.That(wdm, Is.Not.Null);

    uint callCount = 0;
    foreach (var invocation in wdm.Invocations)
    {
        if (invocation.Method.Name == methodName && 
            invocation.Arguments.Count == expectedArgs.Count)
        {
            bool hasSameArgs = true;
            for (int i = 0; i < invocation.Arguments.Count; i++)
            {
                if (!invocation.Arguments[i].Equals(expectedArgs[i]))
                {
                    hasSameArgs = false;
                    break;
                 }
             }

             if (hasSameArgs)
             {
                 callCount++;
             }
        }
    }

    Assert.That(callCount, Is.EqualTo(expectedCallCount));
}

优化方向与实现

1. 利用Mock框架原生断言(假设使用Moq)

如果wdm是Moq的Mock<T>实例,直接用框架自带的Verify方法替代手动遍历,代码更简洁且失败提示更清晰:

private void Then_WirelessDeviceInvokedMethod(
    ushort unitId, 
    string methodName = null, 
    int expectedCallCount = 1, 
    IReadOnlyList<object> expectedArgs = null)
{
    expectedArgs ??= new List<object>();
    var wdm = GetWirelessDeviceMock(unitId);
    Assert.That(wdm, Is.Not.Null);

    var mock = wdm as Mock<IWirelessDevice>; // 替换为你的实际接口类型
    Assert.That(mock, Is.Not.Null, "获取到的Mock对象类型不匹配");

    if (string.IsNullOrEmpty(methodName))
    {
        mock.VerifyAll();
        return;
    }

    // 查找目标方法
    var method = typeof(IWirelessDevice).GetMethod(methodName, expectedArgs.Select(a => a.GetType()).ToArray());
    Assert.That(method, Is.Not.Null, $"未找到名为{methodName}的方法");

    // 构建方法调用表达式
    var instanceParam = Expression.Parameter(typeof(IWirelessDevice), "x");
    var callExpr = Expression.Call(instanceParam, method, expectedArgs.Select(arg => Expression.Constant(arg)).ToArray());
    var actionExpr = Expression.Lambda<Action<IWirelessDevice>>(callExpr, instanceParam);

    mock.Verify(actionExpr, Times.Exactly(expectedCallCount));
}

2. 用LINQ简化手动计数逻辑

如果必须手动遍历Invocations,用LINQ替代嵌套循环,代码更紧凑:

private void Then_WirelessDeviceInvokedMethod(
    ushort unitId, 
    string methodName = null, 
    int expectedCallCount = 1, 
    IReadOnlyList<object> expectedArgs = null)
{
    expectedArgs ??= new List<object>();
    var wdm = GetWirelessDeviceMock(unitId);
    Assert.That(wdm, Is.Not.Null);

    var callCount = wdm.Invocations.Count(invocation => 
        invocation.Method.Name == methodName &&
        invocation.Arguments.Count == expectedArgs.Count &&
        invocation.Arguments.Zip(expectedArgs, (invArg, expArg) => invArg.Equals(expArg)).All(b => b));

    Assert.That(callCount, Is.EqualTo(expectedCallCount), 
        $"方法{methodName}调用次数不符合预期:预期{expectedCallCount}次,实际{callCount}次");
}

3. 增强类型安全(推荐)

避免用字符串硬编码方法名,改用表达式树实现编译期类型检查,杜绝拼写错误:

// 重载方法,支持表达式指定目标方法
private void Then_WirelessDeviceInvokedMethod(
    ushort unitId, 
    Expression<Action<IWirelessDevice>> methodExpr, 
    int expectedCallCount = 1)
{
    var wdm = GetWirelessDeviceMock(unitId);
    var mock = wdm as Mock<IWirelessDevice>;
    Assert.That(mock, Is.Not.Null);
    
    mock.Verify(methodExpr, Times.Exactly(expectedCallCount));
}

// 使用示例
Then_WirelessDeviceInvokedMethod(unitId, x => x.Connect("device-001"), 1);

4. 细节优化

  • 用??空合并运算符简化expectedArgs的空值处理
  • 为断言添加明确的失败提示,降低调试成本
  • 补充methodName为空场景的处理逻辑

内容的提问来源于stack exchange,提问作者Hamza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:58:10