Модульное тестирование кода, использующего базу данных

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

Устанавливаете ли вы базу данных QA (известную отправную точку) перед запуском набора тестов.

ИЛИ ЖЕ

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
43
0
13 873
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

Проделаем несколько хитростей:

edit: У нас есть отдельные базы данных для каждого пользователя. На нашем сервере сборки также есть собственная база данных. Стоимость дополнительных жестких дисков намного меньше, чем стоимость разработчиков, влияющих на тесты друг друга.

  1. Наше приложение может генерировать таблицы и самостоятельно выполнять шаги обновления (без отдельных сценариев db). Это помогает нашему клиенту, потому что ему нужно только добавить файл WAR, и он готов. Приложение просматривает базу данных и выполняет все необходимые операторы DDL перед запуском остальной части контекста Spring.
  2. В нашем наборе тестов есть код, который выгружает контекст Spring, удаляет базу данных и перезапускает контекст с чистой базой данных. Мы можем при желании отключить это, если захотим
  3. Все наши модульные тесты базы данных / SQL - это тесты Spring транзакционной интеграции. Это означает, что после завершения теста транзакции откатываются, и другие модульные тесты снова будут проверять чистую базу данных.

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

В какой-то момент я действительно считаю, что вам нужен доступ к базе данных, потому что это не идеальный / академический мир, в котором мы живем :-)

Зависит от случая, но в устаревших системах, где я не хочу отключать базу данных, я часто использую интерфейс, скажем, IFooDBManager, у которого есть методы, возвращающие объекты ADO.Net, такие как таблицы данных или наборы данных. Конечно, это не обязательно должен быть интерфейс, но это может быть, например, виртуальный метод. Затем в своих тестах я использую небольшой API с плавным интерфейсом, который я построил сам давно, я использую его для создания наборов данных и таблиц и заполнения их тестовыми значениями, чтобы я мог вернуть их из своих подделок.

Свободный интерфейс выглядит примерно так:

return DataTableBuilder.Create()
    .DefineColumns("a, b")
    .AddRow().SetValue("a", 1).SetValue("b", 2).DoneWithRow()
    .AddRow().SetValue("a", 10).SetValue("b", 20).DoneWithRow()
.Table

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

Обновлено: Я забыл уточнить, что это для заглушки базы данных, поэтому взаимодействие с базой данных в этом случае не проверяется. Фактическое взаимодействие будет происходить в конкретной реализации IFooDBManager, для тестирования что необходимо что-то еще полностью.

Кроме того, не все методы в таком интерфейсе, конечно, возвращают вещи, есть также методы для записи в базу данных, и я обычно тестирую их с помощью тестирования взаимодействия в RhinoMocks, где я устанавливаю ожидания для этих методов.

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

Один из фреймворков, над которым мы работаем, - это Rhino Mocks. Вы можете прочитать об этом по ссылке ниже. В нем также есть хорошее представление о том, зачем вам нужны такие фреймворки.

http://weblogs.asp.net/stephenwalther/archive/2008/03/22/tdd-introduction-to-rhino-mocks.aspx

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

Jonathan Allen 25.12.2008 11:52

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

Ather 25.12.2008 14:33
Ответ принят как подходящий

«Заглушка базы данных» обычно называется «фальшивым репозиторием» или «фиктивным репозиторием». Это хорошая идея. Вы можете кодировать их вручную (что несложно для простых случаев) или использовать для их создания фреймворк, например Rhino Mocks. Вы не упоминаете, на каком языке вы работаете. Rhino издевается над .Net.

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

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

Я не возражаю против того, как вы их называете, но лучше держать эти тесты отдельно.

Хорошая идея для обеспечения согласованности данных - начать транзакцию базы данных перед тестом и откатить ее после этого. Таким образом, состояние базы данных возвращается к тому, что было до теста.

Мне нравится трюк с транзакциями.

Jonathan Allen 25.12.2008 11:50

Как мне протестировать программное обеспечение, которое само по себе поддерживает транзакции; то есть код сам по себе совершает несколько транзакций? Есть ли способ провести модульное тестирование, кроме запуска БД в известном состоянии?

Jonathan Leffler 26.12.2008 17:28

Джонатан, здесь в игру вступают вложенные транзакции.

Judah Gabriel Himango 28.01.2009 06:20

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

Venkateswara Rao 11.10.2017 08:33

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

эта ссылка очень интересна для чтения. Спасибо, что поделились этим!

mezoid 24.12.2008 16:38

На самом деле вам следует делать и то, и другое. Когда вы говорите «построить заглушку базы данных», это намекает на насмешку в модульных тестах. А когда вы говорите об «Установить базу данных QA (известная отправная точка)», это намекает на интеграционные тесты, в которых вы действительно попадаете в базу данных. Модульные тесты появляются намного раньше в игре, и именно здесь в игру вступает насмешка. Мокинг выполняется намного быстрее, чем обращение к базе данных. Поэтому, когда вы запускаете много модульных тестов, имитация сэкономит много времени. Rhino Mocks - отличный фреймворк, который я использовал лично. Но есть несколько фреймворков для фиксации, найдите тот, который лучше всего подходит для вас.

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

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

Обычно я использую два подхода.

Код, который зависит от уровня абстракции базы данных, работает с IoC и поэтому легко тестируется с помощью макетов / заглушек. У вас есть интерфейс IDataAccess или IRepository, и вы проверяете взаимодействие своего кода с ним.

Код, который на самом деле работает с базой данных (скажем, класс, реализующий IDataAccess), тестируется сам по себе, обычно выполняя двусторонние обращения к базе данных (вставка-извлечение-обновление-извлечение-удаление). Перед запуском каждого тестового примера база данных воссоздается или очищается, чтобы избежать перекрестных помех между тестами. Это приводит к тестам, для выполнения которых требуется несколько минут вместо нескольких секунд, но, на мой взгляд, важно протестировать фактическую базу данных, которую вы будете использовать в производственной среде. Использование такой замены, как SQLite в памяти, не является хорошим выбором, потому что он слишком отличается от, например, SQL Server.

вот хороший снимок экрана по этой теме называется Ценность небольших тестов

Спасибо за ссылку! отличное видео!

Sam 09.03.2009 02:43

В Spring есть тестовые классы, которые являются транзакционными. Заполните тестовые данные, запустите тесты, откатите их. Как будто тебя там никогда не было.

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

Другие вопросы по теме