Модульное тестирование - приложение, связанное с базой данных

15

Каков наилучший подход при модульном тестировании модели, которая интегрируется в приложение, тесно связанное с базой данных?

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

user1189880
источник
1
Интересно, что за ответы, которые фактически говорят «переписать код приложения», проголосовали
AD7six

Ответы:

10

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

Только не подключайтесь к производственной базе данных во время тестирования!

chrisaycock
источник
1
С DI и надлежащим дизайном приложения вы сможете тестировать без какой-либо базы данных - при условии, что вводимый вами макет обеспечивает достаточно детальный макет внутренней базы данных.
Питер К.
4

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

Без шансов
источник
2

Модель не должна зависеть от (конкретной) БД. Если он знает только абстрактную БД (читай «интерфейс»), которая передается модели, тогда вы можете заменить БД на фиктивный объект .

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

EricSchaefer
источник
1

У меня была похожая проблема - у меня не было возможности гарантировать, что моя тестовая БД сохранит значения. Так что в будущем я получу, например, другие цены.

Я извлек необходимые данные в небольшой sqlite -DB и использовал эту БД для своих тестов. Test-DB теперь является частью настройки моего юнит-теста.

Кнут
источник
2
Цель модульных тестов - тестировать ваш код изолированно. Если вы используете базу данных sqllite, то она не изолирована. Также несоответствия между базами данных могут привести к ошибкам
Том Сквайрс
0

«Наилучшее» субъективно, но вы можете просто использовать тестовое соединение БД.

Используйте приборы для загрузки некоторых тестовых данных (например, продуктов для покупки), а затем напишите тестовый набор для класса / функции, которую вы хотите протестировать.

AD7six
источник
Описание модульных тестов, которые тестируют функцию, которая действует в базе данных как интеграционные тесты, вводит в заблуждение @murph.
AD7six
1
Хорошо, теперь я глубоко сбит с толку - если он включает в себя базу данных, то по большинству определений он не является модульным тестом, поскольку он не самодостаточен. Если у вас есть база данных, то вы запускаете тесты на более высоком уровне, тот, у которого есть зависимости, тот, который рассматривает «объединение» вещей. Несмотря на это, я не могу объяснить, как решить проблему.
Murph
0

Я создал плагин для Symfony 1.4 (PHP) для решения этой проблемы (среди прочих). Он моделируется по принципу работы тестовой среды Django (Python) : среда создает и заполняет отдельную базу данных тестов перед началом каждого теста и уничтожает базу данных тестов после завершения каждого теста.

У меня была пара опасений по поводу этой стратегии, как с точки зрения производительности (если схема не меняется, почему бы просто не очистить данные вместо перестройки всей структуры?), Так и удобства (иногда я хочу проверять базу данных после провал теста, так что не разрушайте его без разбора!), поэтому я выбрал немного другой подход.

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

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

В обеих тестовых средах адаптер базы данных настроен на использование тестового соединения вместо «производственного» соединения для предотвращения повреждения существующих данных при выполнении теста.


источник
0

Я бы сказал, просто продолжайте и используйте приборы для предварительной загрузки данных. Это то, как фреймворки модульного тестирования работают в целом при тестировании манипуляций с данными.

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

Например, вместо удаления SQL непосредственно в код, где он вам нужен, есть способ вызвать метод, который делает только то, что делает этот SQL. Используйте Person.getPhoneNumber(), например, вместо SELECT phone_number FROM person WHERE id = <foo>. Это не только чище и проще для понимания с первого взгляда, но и во время тестирования вы можете смоделировать объект Person, чтобы getPhoneNumber()он всегда возвращал 555-555-5555или что-то в этом роде, а не касался базы данных.

Izkata
источник
0

Это довольно легко сделать с джунитом, если он немного затянут.

«Настройка» должна определять и заполнять набор временных таблиц.

Затем вы можете выполнить модульные тесты для всех функций обновления, вставки, удаления.

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

В фазе «демонтажа» вы отбрасываете все столы.

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

Джеймс Андерсон
источник