Свойства Stubbing с частными сеттерами для тестов

10

У нас есть объект

public class MyObject{
    protected MyObject(){}

    public string Property1 {get;private set;}
    public string Property2 {get;private set;}
    public string Property3 {get;private set;}
    public string Property4 {get;private set;}
    public string Property5 {get;private set;}
    public string Property6 {get;private set;}
    public string Property7 {get;private set;}
    public string Property8 {get;private set;}
    public string Property9 {get;private set;}
    public string Property10 {get;private set;}
}

В нашем производственном коде мы заполняем этот объект через autopper. Он может получить доступ к свойствам и установить их правильно.

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

Есть несколько доступных вариантов.

  • Пользовательские конструкторы, чтобы принять параметры, необходимые для тестов и установить свойства, в настоящее время требуется 3 конструктора. Это не чисто, так как конструкторы не обслуживают бизнес-функции.

  • Сделайте свойства виртуальными, чтобы класс можно было заглушить. Но пометка виртуальных свойств не дает никакой ценности для бизнеса и не загрязняет мой класс.

  • Добавьте в класс конструктор объектов для внутреннего конструирования объекта. Опять нет добавленной стоимости бизнеса. Возможно, немного чище, но все еще много несоответствующего кода в объектах домена.

Любые предложения, советы или альтернативные варианты здесь?

Jman
источник

Ответы:

8

У вас есть несколько вариантов.

  • Идите дальше и используйте пользовательские конструкторы, виртуальные свойства или конструктор объектов. Обоснование этого заключается в том, что объект должен стоять сам по себе и не зависеть от какой-то магии, такой как автопроизводитель. Класс, который совершенно бесполезен, если не происходит никакого волшебства, не очень хорошо продуман. «Бизнес-ценность» - не единственный определяющий фактор хорошего дизайна.

  • Включите автомат в процесс тестирования. Некоторые скажут, что это больше не юнит-тест. Это не имеет значения. Модульное тестирование - не единственный вид тестирования.

  • Реализуйте что-то, что обеспечивает функциональность automapper специально для тестирования. Вы можете легко написать небольшую утилиту, которая будет использовать отражение для заполнения вашего объекта из словаря, содержащего имена и значения свойств.

Также взгляните на этот вопрос и ответ: Вы бы предпочли сделать приватные вещи внутренними / публичными для тестов или использовать какой-нибудь хак, такой как PrivateObject?

Майк Накис
источник
3

Я не стесняюсь использовать рефлексию для подобных вещей в тестах.

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

Я не знаю Autopper, но я согласен с @Mike, что включение его в тесты может быть хорошей идеей. Различение модульных тестов / интеграционных тестов не очень интересно. Конечно, если набор тестов становится большим и медленным, вам нужно будет отфильтровать и классифицировать вещи, чтобы выполнить только разумное подмножество всех тестов с самой высокой частотой.

Пример взлома с использованием отражения, с использованием nameof () будет лучше, но тогда вы потеряете типы:

public static class TestExtensions
{
    public static void SetProperty<TSource, TProperty>(
        this TSource source,
        Expression<Func<TSource, TProperty>> prop,
        TProperty value)
    {
        var propertyInfo = (PropertyInfo)((MemberExpression)prop.Body).Member;
        propertyInfo.SetValue(source, value);
    }
}
Йохан Ларссон
источник
0

Для модульного тестирования используйте фальшивые фреймворки, такие как Microsoft Fakes, TypeMock и JustMock, которые предоставляют поддержку для насмешливых частных пользователей.

Пожалуйста, посмотрите на Smocks (доступные пакеты @nuget). Ограничением Smocks является то, что он не будет предоставлять доступ частным пользователям. Но у него есть возможность издеваться над статичными и не виртуальными членами. Также он доступен бесплатно.

Другой простой способ - использовать PrivateObject / PrivateType.

Картик V
источник