В TDD и DDD, как вы обрабатываете свойства только для чтения в подделках? - PullRequest
5 голосов
/ 08 июня 2009

Вопрос

Как вы обрабатываете поля только для чтения при создании подделок?

Фон

Я нахожусь на начальной стадии использования ASP.Net MVC и в качестве примера использую спортивный магазин Стивена Сандерсона и Notd Dinner Скотта Гу. Одна небольшая проблема, с которой я только что столкнулся, - как работать со свойствами, доступными только для чтения, при подделке. Я использую LINQToSQL.

Мой интерфейс:

public interface IPersonRespository
{   
    Person GetPerson(int id);
}

и моя подделка становится

public class FakePersonRepository
{
    public Person GetPerson(int id)
    {
        return new Person {id="EMP12345", name="John Doe", age=47, ssn=123-45-6789, totalDrWhoEpisodesWatched=42};
    }
}

Вот моя проблема. Поля id, ssn и totalDrWhoEpisodesWatched доступны только для чтения, поэтому приведенный выше код фактически не будет работать. Тем не менее, я не знаю, как создать нового человека и установить свойство только для чтения. Я уверен, что есть решение, но я еще не нашел его в своих поисках.

Обновление: наследование + скрытие свойства как потенциальное решение?

Я еще не определился с твердым решением проблемы. Мне не нравится идея модификации классов моего домена для целей создания подделок. Для меня добавление разметки к классам домена для проведения тестирования - это форма дополнительной связи - связи с реализацией вашего теста. Сейчас я изучаю еще одну возможность - создать класс FakePerson, который наследуется от Person, но скрывает свойства с новыми свойствами чтения-записи.

public class FakePerson: Person
{
    public new int age { get; set; }
    public new string ssn { get; set; }
    public new int totalDrWhoEpisodesWatched { get; set; }
}

Пока это решение, как я склоняюсь. Это нарушает принцип подстановки Лискова, однако это не сильно беспокоит меня в тестовом проекте. Я был бы рад услышать любую критику и / или отзывы по этому поводу в качестве решения.

Победитель: Mock Frameworks

Мок, кажется, делает эту работу. Мое последнее решение - скрыть свойство с помощью наследования - действительно работает, однако, используя Moq, я получаю стандартизированный набор функций, который более удобен в обслуживании. Я предполагаю, что другие фиктивные фреймворки имеют эту функциональность, но я не проверял. Говорят, что Moq более прост для начинающего написания макетов, чем я определенно сейчас являюсь.

Ответы [ 5 ]

6 голосов
/ 09 июня 2009

Рассмотрите возможность насмешки над типом персонажа в тесте. Пример использования Moq :

var mock = new Mock<Person>();
mock.SetupGet(p => p.id).Returns("EMP12345");
mock.SetupGet(p => p.ssn).Returns("123-45-6789");
mock.SetupGet(p => p.totalDrWhoEpisodesWatched).Returns(42);
return mock.Object;

В противном случае попробуйте выяснить, как LINQ to SQL устанавливает эти свойства только для чтения.

РЕДАКТИРОВАТЬ : Если вы попытаетесь выполнить вышеизложенное, и Moq сгенерирует ArgumentException в вызове SetupGet с сообщением "Недопустимая настройка для не перезаписываемого элемента: p => p. id ", тогда вам нужно пометить свойство как virtual . Это необходимо сделать для каждого свойства, геттер которого вы хотите переопределить.

В LINQ to SQL это можно сделать в конструкторе ИЛИ, выбрав свойство, затем в окне «Свойства» задайте Модификатор наследования - виртуальный .

1 голос
/ 08 июня 2009

В .NET вы можете пометить ваши сеттеры как «внутренние» и использовать атрибут сборки InternalsVisibleTo, чтобы сделать внутренние компоненты видимыми для вашей тестовой сборки. Таким образом, ваши сеттеры не будут общедоступными, но вы все равно сможете получить к ним доступ.

примечание: хотя вопрос не помечен как .NET, я предположил, что он основан на использовании вами синтаксиса инициализатора объекта. Если мое предположение было неверным, это предложение не применяется (если, конечно, язык, который вы используете, не имеет эквивалентной функции).

1 голос
/ 08 июня 2009

Вы можете установить свойства только для чтения в конструкторе класса. Объект Person должен иметь конструктор, который принимает id, ssn и totalDrWhoEpisodesWatched. Конечно, если это объект, сгенерированный linqtosql, у вас могут возникнуть проблемы с его изменением, поскольку код генерируется автоматически.

Вы можете рассмотреть возможность использования сопоставленного объекта для показа в своем хранилище ... чтобы вам никогда не пришлось использовать ваш объект linqtosql в качестве модели.

0 голосов
/ 19 августа 2009

Я также использую Moq. Я люблю это, и это прекрасно работает. Но прежде чем я начал использовать Moq, я написал много подделок. Вот как бы я решил проблему с помощью подделок.

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

Как это:

public class FakePersonRepository : IPersonRespository
{
    private IDictionary<int, Person> _people = new Dictionary<int, Person>();

    public Person GetPerson(int id)  // Interface Implementation
    {
        return _people(id);
    }

    public void SetPerson(int id, Person person)  // Not part of interface
    {
         _people.Add(id, person);
    }

}
0 голосов
/ 09 июня 2009

Если это для тестов - подумайте об использовании отражения. Это не повлечет за собой возни с вашей моделью домена.

Например - я получил класс FactoryBase, который использует отражение, чтобы установить необходимый параметр с помощью лямбда-выражения через параметры (например, this ) Работает как шарм - создание новой фабрики просто, как определение типа репозитория и данных сущности по умолчанию.

...