Издеваться над сущностью? - PullRequest
       7

Издеваться над сущностью?

2 голосов
/ 13 сентября 2011

Я где-то слышал, что издеваться над сущностью - ПЛОХАЯ вещь, и вы должны только издеваться над услугами (даже если мы не делаем полномасштабный DDD, а "Сорта" DDD).

Итак, учитывая следующую типичную историю, как вы пишете для нее тест?

  • Если клиент имеет статус Предпочтительный, он должен быть уведомлен о случившемся.

Предпочтительный статус зависит от других вещей, например, клиент недавно приобрел 10 виджетов, или его имя начинается с «A» и т. Д. В любом случае, давайте предположим, что мы уже реализовали эту функцию. Как мне написать тест для вышеупомянутой истории? В частности, меня интересует, как требование тестируемости повлияет на мой дизайн в этом случае.

  1. Я могу использовать какой-то продвинутый макет фреймворка (например, Isolator), который позволяет мне макетировать не виртуальные свойства. Нет корреляции между тестируемостью и дизайном, нет проблем.
  2. Я могу сделать свойство IsPreferred виртуальным и смоделировать его (собственно, заглушку). Не знаю почему, но это кажется грязным. Смотрите вопрос в верхней части поста. Кроме того, не уверен, как это улучшает мой дизайн. 2а. Скройте сущность за интерфейсом ICustomer и смейтесь над ней. Совершенно не круто.
  3. Я могу сделать это свойством чтения-записи, но это было бы очень плохим дизайнерским решением.

Как вы справляетесь с такими историями?

Ответы [ 2 ]

6 голосов
/ 13 сентября 2011

Я прочитал в нескольких книгах / блогах похожую вещь, в которой предлагалось не издеваться над сущностями или объектами из модели предметной области. Насколько я помню, сообщение было не имитируй данные, имитируй поведение . (Я, вероятно, читал это в книге XUnit Test Patterns или GOOS).

Лично я думаю, что если ваша сущность нуждается в большой настройке, ее можно издеваться. Позвольте мне привести пример, представьте, что у вас есть бренды, продукты, цены и наличие на складе. У бренда есть Producs, у продуктов есть цены и запас. Тестируемый класс вызывает метод Brand.IsAvailable (), который, в свою очередь, проверяет наличие хотя бы одного товара с действительной ценой и в наличии (это довольно логично). Поэтому, если вы пытаетесь выполнить модульное тестирование CUT, тестирование всей логики Brand.IsAvailable выходит за рамки возможного, поэтому имеет смысл высмеивать этот метод.

Если сущность легко настроить, тогда используйте сущность, например, если у вас есть пользователь и у него есть свойство с именем active, вы можете создать пользователя и установить это свойство в своем тесте как * 1010. * стоимость создания фальшивого пользователя и присвоения этого свойства, вероятно, такая же, как насмешка над вызовом метода, и это (на мой взгляд) более понятно.

Я не эксперт по c #, но я читал, что Isolator - одна из лучших платформ для .net, поэтому я бы сделал то, что вы упомянули на 1 .

2 голосов
/ 15 сентября 2011

Я бы не ставил слишком много логики на сущности.Они должны заботиться о своем собственном состоянии, например.заботиться о согласованности собственных полей.Но они не должны заботиться о последовательности во всей системе.ИМХО, ваш пример «user.AddOrder», который устанавливает состояние пользователя, заходит слишком далеко.

Вы также теряете возможность повторного использования ваших сущностей, если они все делают.Если вы получаете дополнительные требования (например, дополнительные ситуации, такие как импорт данных, новый тип заказа, новый тип правила), ваша модель сущности мешает, она недостаточно гибкая.

Это отсутствие гибкостиобнаруживается в модульных тестах.Когда вы изолируете классы в модульных тестах, становится ясно, что на самом деле инкапсулировано и больше не может быть разделено.Если у вас возникнут проблемы, ваша инкапсуляция, скорее всего, не очень полезна.Таким образом, модульные тесты являются отличным доказательством возможности повторного использования.

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

class User
{
    AccountState State { get; }
    void MakeTopSeller()
}

class Order
{
    decimal GetTotal();
    User Salesman { get; }
}


class OrderService
{
  void AddOrder()
  {
      // ....
      if (order.GetTotal() > ToSellerAmount)
      {
          order.Salesman.MakeTopSeller();
      }
  }
}

Редактировать : Если вы все еще считаете, что ваш подход является подходящим (я имею в виду, я не могу определиться с тем, что я знаю об этом), вам не нужно его менять.Но если ваша сущность использует другую сущность для установки состояния, и вам нужно это состояние в тесте, вам нужно сделать все это в тесте: создать заказ и добавить его пользователю.

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

...