Rhino Mocking и TDD с устаревшим кодом

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

Моя проблема в том, что у меня есть объект "Vehicle", он довольно прост, но у него нет интерфейсов или чего-то еще. Этот проект был создан до того, как TDD действительно начала становиться более основным потоком. В любом случае, мне нужно добавить новый метод для изменения стартового пробега автомобиля. Я думал, что это будет хорошее начало, чтобы попробовать TDD и Mocking, поскольку я новичок. Моя проблема в том, что мне нужно создать транспортное средство, чтобы выполнить некоторые проверки, связанные с переходом в базу данных. Извините, если мой вопрос не на 100% ясен, почему я отправляю сообщения, так как меня немного смущает, когда Rhino Mocks вписывается (и если мне это нужно!).

Ответ 1

Легко ли создать экземпляр типа Vehicle (объект), а затем вызвать ваш метод для теста? Если да, то, скорее всего, вам не нужен макет.

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

Vehicle  [depends On>] OwnerRepository [satisfied By] SQLOwnerRepository

Итак, вы вводите интерфейс (OwnerRepository, чтобы получить информацию о владельце, скажем), чтобы разделить взаимодействие БД (определить контракт) между ними. Сделайте свою реальную зависимость (здесь SQLOwnerRepository) реализуйте этот интерфейс. Также создайте свой код таким образом, чтобы могли быть введены зависимости, например.

public Vehicle (OwnerRepository ownerRepository) 
{  _ownerRepository = ownerRepository;  // cache in member variable }

Теперь в тестовом коде

Vehicle [depends On >] OwnerRepository [satisifed By] MockOwnerRepository 

У вас есть фреймворки, которые с учетом интерфейса создадут макетную реализацию (см. рамки Rhino/Moq). Таким образом, вам больше не требуется фактическое соединение с БД для проверки вашего класса Vehicle. Mocks используются для абстрагирования отнимающих много времени/неконтролируемых зависимостей, чтобы ваши тесты устройств были быстрыми/предсказуемыми.

Я бы рекомендовал прочитать "Injection of Dependency", чтобы лучше понять, когда и почему использовать mocks.

Ответ 2

Проблема - это зависимости. Ваш класс автомобиля зависит от базы данных. Надеемся, что все взаимодействия с базой данных были инкапсулированы в класс хороший, мы вернемся к этому через секунду. Когда вы запускаете свой unit test, вы хотите испытать класс автомобиля, не заботясь о базе данных. Например, вы хотите проверить, что ваш метод SpeedUp (int x) действительно увеличивает общую скорость на x. В этом методе первое, что он делает, это запрос БД для его текущей скорости. Это означает, что вы должны иметь БД для тестирования! Плотина, это звучит не очень быстро и не повторяется. Также много настроек для запуска теста.

Было бы здорово, если бы у нас была бы притворная БД? Это то, где насмехается. Мы создаем Mock класса, у которого есть все взаимодействие с БД, инкапсулированное. Затем мы настраиваем макет для ответа с предварительно запрограммированным значением. Так, например, когда мы запрашиваем DB для текущей скорости, вы возвращаете 100.

Итак, теперь, когда мы тестируем mock, возвращается 100, и мы можем утверждать, что SpeedUp (int x) принимает 100 и добавляет x к нему.

Ответ 3

Rhino mocks может создавать только mocks из интерфейсов или абстрактных классов, в которых не существует вашего устаревшего кода.

TypeMock может издеваться над чем угодно, но не является бесплатным.

Вы можете использовать Microsoft Moles, чтобы издеваться над ними.

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

НТН

Ответ 4

Не прямой ответ на ваш вопрос. Однако стоит взглянуть на следующее.

Габриэль Шенкер опубликовал о применении TDD в унаследованных системах. PTOM - разработка Браунфилда - создание явных зависимостей

В этой статье объясняется создание dependencies explicit и использование Injection Dependency. Это также говорит о Poor Man’s Dependency Injection. Это необходимо, если существует только конструктор по умолчанию.

Что-то вроде

 public OrderService() : this(
    new OrderRepository(),
    new EmailSender(ConfigurationManager.AppSettings["SMTPServer"])
    )

В статье также рассматривается создание обертки для ConfigurationManager для ее проверки.