Зачем мне писать поддельный класс и unit test он?

Я понимаю необходимость протестировать класс с логикой (например, тот, который может рассчитать скидки), где вы можете проверить фактический класс.

Но я только начал писать модульные тесты для проекта, который будет выступать в качестве репозитория (получить объекты из базы данных). Я нахожу, что пишу "фальшивый" репозиторий, который реализует интерфейс ISomethingRepository. Он использует Dictionary<Guid, Something> для хранения внутри. Он реализует методы Add(Something) и GetById(Guid) интерфейса.

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

Я также получил совет использовать макет-объект, который я могу настроить заранее, чтобы оправдать определенные ожидания. Это кажется еще более бессмысленным для меня: конечно, тест будет успешным, я издевался/подделывал его, чтобы добиться успеха! И я все еще не уверен, что фактическое программное обеспечение будет работать так же, как при подключении к базе данных...

запуталась...

Может ли кто-нибудь указать мне в правильном направлении, чтобы помочь мне понять это?

Спасибо!

Ответ 1

Вы не проверяете свой макет, а какой-то другой класс, который взаимодействует с ним. Таким образом, вы можете, например, проверить, что контроллер перенаправляет вызов метода сохранения в ваш поддельный репозиторий. Что-то не так, если вы "проверяете свои поддельные объекты"

Ответ 2

Не тестируйте класс mock. Протестируйте производственный класс с помощью класса mock.

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

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

Изменить: обновленные термины будут более согласованными.

  • Mock - созданный насмешливым фреймворком
  • Fake - создается вручную, может фактически функционировать.
  • Поддержка тестирования - Mocks, Fakes, Stubs и все остальное. Не производство.

Ответ 3

Цель объекта mock/stub не тестироваться вместо того, который вы пытаетесь протестировать, чтобы вы могли протестировать это устройство, не требуя других классов.

Это в основном так, что вы можете тестировать классы по одному, не тестируя все классы, в которых они также зависят.

Ответ 4

Кто наблюдает за наблюдателями?

Интересно, например, если реализация mock генерирует определенные исключения для угловых случаев, поэтому вы знаете, что классы, которые используют или зависят от IRepositorySomething, могут обрабатывать исключения, которые бросаются в реальной жизни. Некоторые из этих исключений вы не можете легко генерировать с помощью тестовой базы данных.

Вы не проверяете объект Mock с помощью unit test, но используете его для тестирования классов, которые зависят от него.

Ответ 5

Вы не должны тестировать класс mock.

Что вы обычно делаете, так это: вы создаете макеты для всех классов, с которыми взаимодействует класс, с которым вы тестируете.

Скажем, вы тестируете класс под названием Bicycle, который принимает объекты-конструкторы классов Wheel, Saddle, HandleBar и т.д.

И затем в классе Bike вы хотите протестировать свой метод GetWeight, который, вероятно, выполняет итерацию через каждую часть и вызывает свойство/метод Weight of the, а затем возвращает итоговое значение.

Что вы делаете:

  • вы пишете макет класса для каждой части (Колесо, седло и т.д.), Которые просто реализует бит веса
  • тогда вы передаете эти макетные классы на велосипед
  • проверьте метод GetWeight на классе Bicycle

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

Ответ 6

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

Ответ 7

Я действительно нашел два использования для макетов, которые мы используем при тестировании реализации репозитория.

Во-первых, необходимо проверить службы, которые используют реализацию эквивалента "ISomethingRepository" , который вы упомянули. Однако наши реализации репозитория создаются с помощью factory. Это означает, что мы пишем тесты против "ISomethingRepository" , но не против "MockSomethingRepository" напрямую. Путем тестирования с интерфейсом мы можем легко утверждать, что покрытие кода для наших тестов охватывает 100% интерфейса. Обзор кода обеспечивает простую проверку того, что тестируются новые интерфейсные элементы. Даже если разработчики работают против макета, возвращаемого factory, сервер сборки имеет другую конфигурацию, которая проверяет конкретную реализацию, которую factory возвращает в ночных сборках. Он обеспечивает лучшее из обоих миров с точки зрения охвата тестированием и локальной производительности.

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

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

Конечно, YMMV.; -)

Ответ 8

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

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

Ответ 9

Посмотрите следующую статью для хорошего объяснения этого:

https://web.archive.org/web/20110316193229/http://msdn.microsoft.com/en-us/magazine/cc163358.aspx

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

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

Ответ 10

Как правило, нет необходимости запуска классических модульных тестов на уровне доступа к данным. Возможно, вы можете написать интеграционный стиль unit test для ваших классов данных, то есть Integration Test (= интегрировать свой код уровня доступа к данным с помощью БД), используя функции модулей тестирования модулей.

Например, в проекте Spring вы можете использовать Spring Testcontext, чтобы начать свой Spring контекст внутри unit test, а затем подключиться к реальной базе данных и проверить, что запросы возвращают корректные результаты. Вам, вероятно, нужна собственная база данных для модульных тестов, или, возможно, вы можете подключить их к базе данных разработчика.