Насмешливый интерфейс не имеет смысла? - PullRequest
1 голос
/ 17 июня 2019

Я новичок в модульном тестировании, и мне кажется, что мне здесь чего-то не хватает. Я хотел бы проверить результат DoSomethingWithArray ниже:

class Traffic:ITraffic
{
    private HugeArray _hugeArray;
    public Traffic(HugeArray hugeArray)
    {
        _hugeArray = hugeArray;

    }

    public int DoSomethingWithArray()
    {
        var ret = 0;
        //Here some code that does something with big array and stores some integer values in ret
        return ret;
    }
}

class HugeArray
{
    //This is my custom data structure;
}

interface ITraffic
{
    int DoSomethingWithArray();
}

Я использую Nunit, и из того, что я прочитал, лучше издеваться над интерфейсами, чем над классами. Моя проблема в том, что я хотел бы проверить, как БЕТОННОЕ функционирование DosomethingWithArray в классе Traffic, мне трудно понять, как может вписаться интерфейс ITraffic. Что мне здесь не хватает?

РЕДАКТИРОВАТЬ Вот как я могу проверить свой класс

[TestFixture]
public class TrafficTests
{
    private Traffic _traffic;
    private const int size = 1000000;
    private const int key = 1851925790;

    [OneTimeSetUp]
    public void Setup()
    {
        var hugeArray = new HugeArray(size);
        //Some Setups to create an edge case, not  relevant to my question
        hugeArray.AddValue(size - 1, Int.MaxValue);
        hugeArray.AddValue(size - 2, key);
        //This is the object I want to test, 
        _traffic = new Traffic(hugeArray);
    }

    [Test]
    public void DoSomethingWithArray_Test()
    {
        Assert.DoesNotThrow(() =>
                            {
                                var ret = _traffic.DoSomethingWithArray();
                                Assert.AreEqual(ret, 233398);
                            });
    }



} 

мой вопрос: кажется ли этот подход правильным? все в порядке, объект создан для теста, или я должен вместо этого издеваться над интерфейсом ITraffic?

1 Ответ

3 голосов
/ 17 июня 2019

В вашем примере вы тестируете публичный метод Traffic.То, что Traffic реализует ITraffic, не имеет значения.Если вы удалили : ITraffic из класса, чтобы он больше не реализовывал этот интерфейс, это не изменит того, как вы тестировали Traffic.

Вы тестируете Traffic.Мы не высмеиваем то, что тестируем.Мы высмеиваем то, что мы не тестируем.

Допустим, у меня есть этот класс, который проверяет адрес:

public class AddressValidator
{
    public ValidationResult ValidateAddress(Address address)
    {
        var result = new ValidationResult();

        if(string.IsNullOrEmpty(address.Line1))
            result.AddError("Address line 1 is empty.");
        if(string.IsNullOrEmpty(address.City))
            result.AddError("The city is empty.");

        // more validations

        return result;
    }
}

Не имеет значения, является ли этот классреализует интерфейс или нет.Если я тестирую этот класс, то нечего издеваться.

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

public interface IPostalCodeValidator
{
    ValidationResult ValidatePostalCode(Address address);
}

public class AddressValidator
{
    private readonly IPostalCodeValidator _postalCodeValidator;

    public AddressValidator(IPostalCodeValidator postalCodeValidator)
    {
        _postalCodeValidator = postalCodeValidator;
    }

    public ValidationResult ValidateAddress(Address address)
    {
        var result = new ValidationResult();

        if (string.IsNullOrEmpty(address.Line1))
            result.AddError("Address line 1 is empty.");
        if (string.IsNullOrEmpty(address.City))
            result.AddError("The city is empty.");

        var postalCodeValidation = _postalCodeValidator.ValidatePostalCode(address);
        if (postalCodeValidation.HasErrors)
            result.AddErrors(postalCodeValidation.Errors);

        return result;
    }
}

Проверка почтового кода достаточно сложна, так что он будет в своем собственном классе со своими собственными тестами.Когда мы тестируем AddressValidator, мы не хотим проверить валидатор почтового индекса.Мы просто хотим протестировать этот класс изолированно, а другой класс отдельно.В пределах AddressValidator мы хотим убедиться, что вызывается _postalCodeValidator.ValidatePostalCode, и что если он возвращает ошибки, мы добавляем их в результат проверки.

Мы не тестируем IPostalCodeValidator (или его реализации) здесь, поэтому мы высмеиваем это.Например, используя Moq :

public void AddressValidator_adds_postal_code_errors()
{
    var postalCodeError = new ValidationResult();
    postalCodeError.AddError("Bad!");
    postalCodeError.AddError("Worse!");

    var postalCodeValidatorMock = new Mock<IPostalCodeValidator>();
    postalCodeValidatorMock.Setup(x => x.ValidatePostalCode(It.IsAny<Address>()))
        .Returns(postalCodeError);

    var subject = new AddressValidator(postalCodeValidatorMock.Object);
    var result = subject.ValidateAddress(new Address());

    Assert.IsTrue(result.Errors.Contains("Bad!"));
    Assert.IsTrue(result.Errors.Contains("Worse!"));
}

На самом деле мы не проверяем почтовый индекс.Мы просто говорим, что для проверки валидатор почтового кода всегда будет возвращать эти две ошибки.Затем мы удостоверимся, что AddressValidator вызывает его и делает то, что мы ожидаем, что будет делать с этими ошибками.

Это в основном то, что макет.Это фальшивая реализация чего-то, что делает что-то простое, например, постоянный ответ, так что мы можем быть уверены, что обработаем этот постоянный ответ так, как мы ожидаем.Если AddressValidator обрабатывает результат правильно, то он работает правильно.Это сделано.

Чтобы убедиться, что валидатор почтового кода real возвращает правильный результат, мы можем написать тесты для этого класса.Таким образом, каждый класс делает что-то простое и имеет тесты, чтобы убедиться, что он делает свое дело правильно.Когда мы соберем их все вместе, гораздо более вероятно, что все это сработает.Если мы нарушим реализацию из IPostalCodeValidator, то тесты для этого класса не пройдут, но тесты для AddressValidator все равно пройдут.Таким образом, мы быстро понимаем, какая часть сломалась, потому что все они тестируются изолированно, поэтому нам не нужно запускать и отлаживать большое количество кода, пытаясь выяснить, в чем проблема.

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