如何在 NSubstitute 3.1 中使用 ref 参数引发委托事件?

How to raise a delegate event using a ref parameter in NSubstitute 3.1?

我正在开发一个使用第 3 方库的 C# 项目。这个库使用 ref 参数定义了一个相当不寻常的委托事件:

event GetDataHandler OnGetData;
public delegate bool GetDataHandler(string name, ref byte[] data);

我正尝试通过 NSubstitute(版本 3.1)在单元测试中引发此事件,但我无法让它工作。我尝试了这段代码(基本上我能想到的所有变体):

var theKey = "test";
byte[] theData = null;
_theObject.OnGetData += Raise.Event<GetDataHandler>(theKey, ref theData);

但这不编译。编译器说:参数 2 不能用 'ref' 关键字传递

我知道 out/ref 机制随着 NSubstitute 4.x 发生了变化,但我的公司还没有升级到更新的版本。

有什么方法可以使用 NSubstitute 3.1 来实现并运行?非常感谢!

此致, 奥利弗

Raise.Event 重载将参数作为 params object[]。我们可以将 byref 字节数组作为此 params 数组中的标准值传递(这意味着我们不会为我们传递的事件参数获得编译时安全性,但如果我们弄错了,测试会很快发现它:)):

_theObject.OnGetData += Raise.Event<GetDataHandler>("name", theData);

这是一个可执行示例:

using NSubstitute;
using Xunit;

public delegate bool GetDataHandler(string name, ref byte[] data);
public interface ISomeType {
    event GetDataHandler OnGetData;
}
public class SampleFixture {

    string lastNameUsed = "";
    byte[] lastBytesUsed = new byte[0];

    [Fact]
    public void SomeTest() {
        var sub = Substitute.For<ISomeType>();
        var data = new byte[] { 0x42 };           
        sub.OnGetData += Sub_OnGetData;

        sub.OnGetData += Raise.Event<GetDataHandler>("name", data);

        Assert.Equal("name", lastNameUsed);
        Assert.Equal(data, lastBytesUsed);
    }

    private bool Sub_OnGetData(string name, ref byte[] data) {
        lastNameUsed = name;
        lastBytesUsed = data;
        return true;
    }
}

在评论中提供更多信息后进行编辑。

我认为 NSubstitute 不支持检查在这种情况下返回的值。

在不知道您要测试什么的情况下,我可以建议一些通用方法来测试这类事情。

第一个选项是手动编写一个您可以完全控制的测试替身(在本例中是 ISomeType 的实现)。除非界面很大,否则我会推荐这种方法。

另一种选择是分别测试代理和接线。例如,给定此 class:

public class ClassUnderTest {
    public ClassUnderTest(ISomeType dep) {
        dep.OnGetData += Dep_OnGetData;
    }

    public static bool Dep_OnGetData(string name, ref byte[] data) {
        data = System.Text.Encoding.UTF8.GetBytes(name);
        return true;
    }
}

我们可以独立测试委托,然后测试我们是否连接了该委托:

[Fact]
public void TestDelegate() {
    byte[] data = new byte[0];
    var result = ClassUnderTest.Dep_OnGetData("hi", ref data);

    Assert.True(result);
    Assert.Equal(new byte[] { 104, 105 }, data);
}

[Fact]
public void TestWireup() {
    var sub = Substitute.For<ISomeType>();
    var subject = new ClassUnderTest(sub);

    sub.Received().OnGetData += ClassUnderTest.Dep_OnGetData;
}

我认为本例中的委托测试可能非常有用,但接线测试可能不是很好,因为它非常针对特定的实现,而不是 behaviour/outcome 所要求的。但在这种情况下很难观察到特定的效果,所以这是一个潜在的答案。

第三,我们可以对有问题的库使用更可测试的包装器。或者这个测试可能完全处于错误的级别 - 警惕我们不拥有的模拟类型。 (我已经写了一些关于这个 here。)

如果您能提供更多关于您在这种情况下要测试的内容的信息,我很乐意尝试提出更合理的答案。 :)