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

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

Ответ 1

Я действительно использовал ваш первый подход с довольно большим успехом, но несколько по-другому, который, я думаю, разрешил бы некоторые из ваших проблем:

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

  • Используйте сервер непрерывной интеграции для построения схемы базы данных, загрузки образцов данных и запуска тестов. Вот как мы храним нашу тестовую базу данных в синхронизации (перестраиваем ее при каждом тестовом прогоне). Хотя для этого требуется, чтобы сервер CI имел доступ к собственному выделенному экземпляру базы данных и владел им, я говорю, что наличие нашей схемы db, построенной 3 раза в день, значительно помогло найти ошибки, которые, вероятно, не были бы найдены до момента доставки (если не позже). Я не могу сказать, что я перестраиваю схему перед каждой фиксацией. Кто-нибудь? При таком подходе вам не придется (ну, может быть, нам следует, но это не очень важно, если кто-то забывает).

  • Для моей группы пользовательский ввод выполняется на уровне приложения (а не db), поэтому он проверяется с помощью стандартных модульных тестов.

Загрузка копии базы данных для производства:
Это был подход, который использовался на моей последней работе. Это была огромная боль из-за нескольких проблем:

  • Копия будет устаревшей из производственной версии
  • Изменения будут внесены в схему копирования и не будут распространяться на производственные системы. На этом этапе у нас были бы расходящиеся схемы. Не весело.

Mocking Database Server:
Мы также делаем это на моей нынешней работе. После каждой фиксации мы выполняем модульные тесты против кода приложения, в который вводится макет db accessors. Затем три раза в день мы выполняем полную конструкцию db, описанную выше. Я определенно рекомендую оба подхода.

Ответ 2

Я всегда выполняю тесты против DB в памяти (HSQLDB или Derby) по следующим причинам:

  • Это заставляет вас думать, какие данные хранить в тестовой БД и почему. Просто вытаскивание вашей производственной БД в тестовую систему переводится как "Я не знаю, что я делаю, или почему, и если что-то ломается, это не я!";)
  • Это гарантирует, что база данных может быть воссоздана с небольшими усилиями в новом месте (например, когда нам нужно реплицировать ошибку с производства)
  • Он чрезвычайно помогает качеству файлов DDL.

В памяти БД загружаются свежие данные после запуска тестов и после большинства тестов я вызываю ROLLBACK, чтобы сохранить его стабильным. ВСЕГДА сохраняйте данные в тестовой базе данных стабильными! Если данные все время меняются, вы не можете проверить.

Данные загружаются из SQL, базы данных шаблонов или дампа/резервной копии. Я предпочитаю дампы, если они находятся в читаемом формате, потому что я могу поместить их в VCS. Если это не сработает, я использую CSV файл или XML. Если мне придется загружать огромное количество данных... я этого не делаю. Вам никогда не придется загружать огромное количество данных:) Не для модульных тестов. Тесты производительности - это еще одна проблема, и применяются разные правила.

Ответ 3

Я задавал этот вопрос в течение длительного времени, но я думаю, что для этого нет серебряной пули.

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

Основная проблема, которую я вижу в этом подходе, заключается в том, что вы охватываете только код, который взаимодействует с вашим уровнем DAO, но никогда не тестирует сам DAO, и, по моему опыту, я вижу, что на этом уровне происходит много ошибок Что ж. Я также держу несколько единичных тестов, которые работают против базы данных (ради использования TDD или быстрого тестирования локально), но эти тесты никогда не запускаются на моем сервере непрерывной интеграции, поскольку мы не храним базу данных для этой цели, и я думаю, что тесты, выполняемые на сервере CI, должны быть автономными.

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

Несмотря на то, что нет никакого вопроса, этот подход улучшит ваше покрытие, есть несколько недостатков, поскольку вы должны быть как можно ближе к ANSI SQL, чтобы он работал как с вашей текущей СУБД, так и с встроенной заменой.

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

Ответ 4

Даже если есть инструменты, которые позволяют так или иначе издеваться над вашей базой данных (например, jOOQ MockConnection, который можно увидеть в этом ответе - отказ от ответственности, я работаю для поставщика jOOQ), Я бы посоветовал не высмеивать большие базы данных со сложными запросами.

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

  • Синтаксис
  • Сложность
  • order (!)

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

Ответ 5

Я использую первый (запуск кода с тестовой базой данных). Единственная существенная проблема, которую я вижу, вы поднимаете с помощью этого подхода, - это возможность выхода из синхронизации синхронизации, с которой я сталкиваюсь, сохраняя номер версии в моей базе данных и делая все изменения схемы с помощью script, который применяет изменения для каждой версии приращение.

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

Ответ 6

Для проекта на основе JDBC (прямо или косвенно, например JPA, EJB,...) вы можете макетировать не всю базу данных (в таком случае было бы лучше использовать тестовый db на реальной РСУБД), но только макет на уровне JDBC.

Преимущество - это абстракция, которая идет именно так, поскольку данные JDBC (набор результатов, количество обновлений, предупреждение,...) одинаковы независимо от того, что такое бэкэнд: ваш prod db, тестовый db или только некоторые данные макета для каждого тестового случая.

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

Acolyte framework включает драйвер JDBC и утилиту для такого макета: http://acolyte.eu.org.