Какие способы макетировать структуру, чтобы получить класс в модульных тестах? - PullRequest
0 голосов
/ 16 февраля 2019

У меня есть класс, который я пытаюсь пройти в модульных тестах.Класс представляет структуру как публичную собственность.Структура также имеет несколько открытых методов (которые делают намного больше, чем метод в структуре).Я не могу вносить изменения в структуру (я не владею этим кодом, и это было бы слишком рискованно в данный момент).

Макет не работает с типами значений.Есть ли способ эффективно "издеваться над структурой"?

public class SystemUnderTest
{
    public FragileStructCantChange myStruct {get; set;}

    public string MethodUnderTest()
    {
        if(myStruct.LongRunningMethod() == "successful")
            return "Ok";
        else
            return "Fail";        
    }
}

public struct FragileStructCantChange
{
    public string LongRunningMethod()
    {
        var resultString = DoStuff(); //calls other subsystems            
        return resultString;
    }
}

1 Ответ

0 голосов
/ 16 февраля 2019

Ссылка на структуру эффективно превращает ее в ссылочный тип через процесс, называемый "бокс" .Упаковка структуры может вызвать некоторые незначительные изменения в поведении, которые вы должны прочитать о , прежде чем принимать решение.

Другой вариант - добавить некоторую косвенность между структурой и тестируемым кодом.Один из способов - добавить a, чтобы указать тестовое значение.

public class SystemUnderTest
{
    public FragileStructCantChange myStruct { get; set; }

    // This value can be overridden for testing purposes
    public string LongRunningMethodOverride { get; set; }

    public string MethodUnderTest()
    {
        // Call the indirect Func instead of referencing the struct directly
        if (LongRunningMethod() == "successful")
            return "Ok";
        else
            return "Fail";
    }

    private string LongRunningMethod()
        => LongRunningMethodOverride ?? myStruct.LongRunningMethod();
}

public class Tests
{

    [Fact]
    public void TestingSideDoor()
    {
        var sut = new SystemUnderTest();

        // Override the func to supply any test data you want
        sut.LongRunningMethodOverride = "successful";

        Assert.Equal("Ok", sut.MethodUnderTest());
    }
}

Другой способ - предоставить перезаписываемый указатель на функцию ...

public class SystemUnderTest
{
    public FragileStructCantChange myStruct { get; set; }

    // This Func can be overridden for testing purposes
    public Func<string> LongRunningMethod;

    public SystemUnderTest() {
        LongRunningMethod = () => myStruct.LongRunningMethod();
    }

    public string MethodUnderTest()
    {
        // Call the indirect Func instead of referencing the struct directly
        if (LongRunningMethod() == "successful")
            return "Ok";
        else
            return "Fail";
    }
}

public class Tests
{

    [Fact]
    public void TestingSideDoor()
    {
        var sut = new SystemUnderTest();

        // Override the func to supply any test data you want
        sut.LongRunningMethod = () => "successful";

        Assert.Equal("Ok", sut.MethodUnderTest());
    }
}

Другой вариант - использовать виртуальный метод., который может быть подделан подклассом или поддельной структурой ( Moq , в этом примере) ...


public class SystemUnderTest
{
    public FragileStructCantChange myStruct { get; set; }

    // This method can be overridden for testing purposes
    public virtual string LongRunningMethod()
        => myStruct.LongRunningMethod();

    public string MethodUnderTest()
    {
        // Call the indirect method instead of referencing the struct directly
        if (LongRunningMethod() == "successful")
            return "Ok";
        else
            return "Fail";
    }
}

public class Tests
{

    [Fact]
    public void TestingSideDoor()
    {
        var sut = new Mock<SystemUnderTest>();

        // Override the method to supply any test data you want
        sut.Setup(m => m.LongRunningMethod())
            .Returns("successful");

        Assert.Equal("Ok", sut.Object.MethodUnderTest());
    }
}

Я не буду утверждать, что любой из этих вариантов довольнои я бы подумал о безопасности перед тем, как поместить этот вид бэкдора в общую библиотеку.Интерфейс обычно предпочтительнее, когда он жизнеспособен.

Подобные вещи работают, хотя, и я думаю, что это может быть справедливым компромиссом, когда вы столкнетесь с недружественным к тестам кодом, который вы не хотите агрессивно использовать.реорганизовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...