В модульном тестировании зачем мне дважды создавать репозиторий?

10

На днях я немного читал о модульном тестировании и видел несколько примеров, когда люди создают интерфейс репозитория (т.е. IExampleRepository), а затем создают реальный репозиторий ( public class ExampleRepository : IExampleRepository) и репозиторий, который будет использоваться для модульного тестирования ( FakeExampleRepository : IExampleRepository).

В IExampleRepositoryних были реализованы те же методы, что и в ExampleRepository, но с разными запросами Linq.

Какова именно цель здесь? Я думал, что одна часть модульного тестирования вашего кода - убедиться, что метод работает правильно? Но когда я использую два совершенно разных запроса, один для «реального» и один в тесте, какой смысл имеет этот тест?

Джао
источник

Ответы:

8

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

Тем не менее, вам также нужно тестировать с «реальным» хранилищем *, но обычно это делается в интеграционном / системном тесте.

* очевидно реальный, как в репо, настроенном на тестирование, возможно, не на производственной БД.

JK.
источник
Таким образом, в модульном тестировании я не буду тестировать сам метод, чтобы убедиться, что он возвращает правильные значения (на основе поддельных / поддельных данных)?
Яо
да, вы будете тестировать сам метод, но только код в методе, код в других объектах должен охватываться в других модульных тестах, взаимодействие нескольких объектов должно охватываться в интеграционных тестах или тестах более высокого уровня
jk.
1
Итак, если я правильно понимаю, я должен использовать исходный репозиторий при модульном тестировании репозиториев. Мне не нужно , чтобы проверить их , когда я пишу тесты для контроллеров (в случае Asp.Net MVC)
Жао
4
@Theomax зависит от контекста: если вы модульного тестирования программного компонента , который не СВОЙ ExampleRepository, то лучше использовать издеваться. Оправдание в том, что вы не тестируете хранилище модульно, а что-то еще.
Андрес Ф.
5
@Theomax Чтобы расширить комментарий AndresF.: Если вы проводите модульное тестирование ExampleRepository, используйте настоящее. Если вы проводите модульное тестирование RepositoryController, то оно должно использовать только тот, FakeExampleRepositoryкоторый возвращает предварительно заданные значения. Таким образом, если ошибка распространяется ExampleRepository, только этот модульный тест не пройдёт - RepositoryControllerтесты продолжатся успешно, так что вы знаете, что ошибки там нет. Если бы контроллер использовал реальный репозиторий, они оба потерпели бы неудачу, и вы бы не знали, был ли у вас 1 баг или 2
Izkata
5

Я согласен с двумя ответами от JK. и Ян Худек - они дают действительно хорошую информацию. Но я думал, что добавлю немного.

Ваш первый вопрос («Что именно является целью здесь?») Важен. В случае, если вы описываете, реальная цель состоит в том, чтобы протестировать классы, использующие IExampleRepositoryинтерфейс, а не тестировать реализации репозитория. Создание FakeExampleRepositoryпозволяет вам тестировать эти клиентские классы, не беспокоясь о деталях реального класса репозитория.

Это особенно верно, если объект, который вы пытаетесь настроить, затрудняет тестирование (например, доступ к файловой системе, вызов веб-службы или связь с базой данных). Используя интерфейсы (и другие подобные методы), вы сохраняете низкий уровень связи. Следовательно, класс Xдолжен знать только об интерфейсе и не должен знать о деталях реализации. Цель состоит в том, чтобы убедиться, что класс Xделает правильные вещи.

Насмешка (или заглушка, подделка ... есть нюансы) - это мощный инструмент для модульного тестирования и TDD. Но может быть боль вручную создавать и поддерживать эти реализации. Таким образом, большинство языков теперь имеют библиотеки-насмешки, чтобы помочь. Поскольку вы используете C #, я бы порекомендовал Moq, потому что он простой и очень мощный. Затем вы можете протестировать интерфейс без накопления лишнего кода для ложных реализаций.

Аллан
источник
the real objective is to test the classes that are utilizing the IExampleRepository interfaceэто не совсем верно. Цель состоит в том, чтобы протестировать его независимо от IExampleRepository. +1 за рекомендацию хорошей структуры изоляции, хотя.
StuperUser
1
Я не согласен. Это не зависит от того, IExampleRepositoryпотому что тестируемый класс связан с этим интерфейсом. Но он не зависит от каких-либо реализаций интерфейса. Я признаю, что мое объяснение, возможно, могло бы использовать немного больше изящества все же. :)
Аллан
5

Какова именно цель здесь?

Изоляция.

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

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

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

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

StuperUser
источник
Подробнее о подделках, издевательствах и заглушках: stackoverflow.com/questions/346372/…
StuperUser
4

Есть три причины, по которым вы можете захотеть предоставить фиктивный экземпляр для модульного тестирования:

  1. Вы хотите ограничить область действия теста, чтобы на тест не влияли ошибки в depndee, возможно, потому что он еще не завершен или нестабилен, или вы не хотите, чтобы ошибки в чьем-либо другом влияли на ваши тесты.
  2. Зависимого сложно настроить. Например, уровень доступа к данным часто макетируется, потому что реальный требует настройки тестовой базы данных. Вам все еще нужно протестировать реальный уровень доступа к данным, но вы можете ограничить дорогостоящие настройки при отладке других вещей.
  3. Чтобы проверить, что зависимый класс правильно реагирует на различные виды ошибок, вы предоставляете фиктивную версию, которая возвращает всевозможные неправильные ответы. Потому что многие виды отказов довольно сложно воспроизвести, но все же следует их протестировать.
Ян Худек
источник