Как да повдигна делегирано събитие с помощта на ref параметър в NSubstitute 3.1?

Работя върху проект на 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? Благодаря много!

С най-добри пожелания, Оливър


person Baldewin    schedule 19.08.2019    source източник


Отговори (1)


Това Raise.Event претоварване приема параметри като params object[]. Можем да предадем масива от байтове byref като стандартна стойност в този масив от параметри (което означава, че не получаваме безопасност по време на компилация за аргументите на събитието, които предаваме, но тестът ще вземе това доста бързо, ако го объркаме :) ):

_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), върху който имате пълен контрол. Освен ако интерфейсът не е огромен, бих препоръчал този подход.

Друг вариант е да тествате делегата и свързването отделно. Например, като се има предвид този клас:

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;
}

Мисля, че тестът за делегат в този случай е потенциално много полезен, но тестът за свързване вероятно не е страхотен, защото е много специфичен за конкретната реализация, а не за изискваното поведение/резултат. Но в този случай наблюдението на конкретния ефект е трудно, така че това е потенциален отговор.

Трето, може да сме в състояние да използваме по-тестваща обвивка върху въпросната библиотека. Или този тест може да е на напълно грешно ниво - внимавайте с подигравателни типове, които не притежаваме. (Написах малко за това тук.)

Ако можете да предоставите малко повече информация за това, което се опитвате да тествате в този сценарий, ще се радвам да се опитам да измисля по-разумен отговор. :)

person David Tchepak    schedule 19.08.2019
comment
Здравей Дейвид, благодаря за отговора. Проблемът обаче е, че реалният манипулатор на събития в тествания клас присвоява на data. Предполагам, че това е причината разработчикът на библиотеката на трета страна да е използвал ref параметър на първо място (макар че out параметър вероятно щеше да е по-добре). Въпреки че, разбира се, е възможно да се повдигне събитието без ключовата дума ref, тогава не мога да проверя дали моят манипулатор на събития е присвоил правилните данни на data. - person Baldewin; 20.08.2019
comment
Здравей @Baldewin, благодаря за разяснението. Добавих някои други опции към отговора. Не е идеално, знам, но се надявам да помогне малко. - person David Tchepak; 20.08.2019
comment
Здравей @David, благодаря много. Харесва ми идеята да тествам делегата отделно! - person Baldewin; 20.08.2019