Я работаю со многими веб-приложениями, которые управляются базами данных различной сложности на сервере. Как правило, есть слой ORM, отдельный от бизнес-логики и логики представления. Это делает модульное тестирование бизнес-логики довольно простым; все может быть реализовано в дискретных модулях, и любые данные, необходимые для теста, могут быть сфальсифицированы посредством моделирования объектов.
Но тестирование ORM и самой базы данных всегда было чревато проблемами и компромиссами.
За прошедшие годы я попробовал несколько стратегий, ни одна из которых не удовлетворила меня полностью.
Загрузите тестовую базу данных с известными данными. Запустите тесты на ORM и убедитесь, что вернулись правильные данные. Недостатком здесь является то, что ваша тестовая БД должна идти в ногу с любыми изменениями схемы в базе данных приложения и может быть не синхронизирована. Он также опирается на искусственные данные и может не отображать ошибок, возникающих из-за глупого пользовательского ввода. Наконец, если тестовая база данных мала, она не будет показывать неэффективность, например отсутствующий индекс. (Хорошо, последнее не совсем то, для чего следует использовать модульное тестирование, но это не повредит.)
Загрузите копию рабочей базы данных и протестируйте ее. Проблема здесь в том, что вы можете не знать, что находится в производственной базе данных в любой момент времени; Ваши тесты, возможно, придется переписать, если данные со временем меняются.
Некоторые люди отмечают, что обе эти стратегии основаны на конкретных данных, и модульное тестирование должно проверять только функциональность. С этой целью я видел, предложил:
- Используйте фиктивный сервер базы данных и проверяйте только то, что ORM отправляет правильные запросы в ответ на данный вызов метода.
Какие стратегии вы использовали для тестирования приложений на основе базы данных, если таковые имеются? Что сработало лучше для вас?
источник
Ответы:
На самом деле я использовал ваш первый подход довольно успешно, но я думаю, что это немного по-другому решит некоторые из ваших проблем:
Сохраните всю схему и сценарии для ее создания в системе контроля версий, чтобы любой мог создать текущую схему базы данных после проверки. Кроме того, храните образцы данных в файлах данных, которые загружаются частью процесса сборки. Когда вы обнаружите данные, которые вызывают ошибки, добавьте их в пример данных, чтобы убедиться, что ошибки не появляются снова.
Используйте сервер непрерывной интеграции для построения схемы базы данных, загрузки образцов данных и запуска тестов. Таким образом мы синхронизируем нашу тестовую базу данных (перестраивая ее при каждом запуске теста). Хотя для этого требуется, чтобы сервер CI имел доступ и владел своим собственным выделенным экземпляром базы данных, я говорю, что построение нашей схемы БД 3 раза в день значительно помогло найти ошибки, которые, вероятно, не были бы обнаружены непосредственно перед доставкой (если не позже). ). Я не могу сказать, что перестраиваю схему перед каждым коммитом. Есть кто-нибудь? При таком подходе вам не придется (ну, может быть, мы должны, но это не имеет большого значения, если кто-то забудет).
Для моей группы пользовательский ввод осуществляется на уровне приложения (не дБ), поэтому это проверяется с помощью стандартных модульных тестов.
Загрузка производственной копии базы данных:
это был подход, который использовался на моей последней работе. Это было огромной болезненной причиной нескольких проблем:
Mocking Database Server:
мы также делаем это на моей текущей работе. После каждого коммита мы выполняем модульные тесты для кода приложения, в который введены фиктивные средства доступа к БД. Затем три раза в день мы выполняем полную сборку БД, описанную выше. Я определенно рекомендую оба подхода.
источник
Я всегда запускаю тесты для БД в памяти (HSQLDB или Derby) по следующим причинам:
БД в памяти загружается свежими данными после запуска тестов и после большинства тестов, я вызываю ROLLBACK, чтобы сохранить его стабильность. ВСЕГДА сохраняйте данные в тестовой БД стабильными! Если данные все время меняются, вы не можете проверить.
Данные загружаются из SQL, шаблонной БД или дампа / резервной копии. Я предпочитаю дампы, если они в удобочитаемом формате, потому что я могу поместить их в VCS. Если это не работает, я использую файл CSV или XML. Если мне нужно загружать огромные объемы данных ... я не делаю. Вам никогда не придется загружать огромные объемы данных :) Не для модульных тестов. Тесты производительности являются еще одной проблемой, и применяются другие правила.
источник
Я давно задавал этот вопрос, но думаю, что серебряной пули для этого не существует.
В настоящее время я занимаюсь насмешкой над объектами DAO и сохранением в памяти хорошего набора объектов, представляющих интересные случаи данных, которые могут храниться в базе данных.
Основная проблема, которую я вижу при таком подходе, заключается в том, что вы покрываете только код, взаимодействующий с вашим уровнем DAO, но никогда не тестируете сам DAO, и по своему опыту я вижу, что на этом уровне также происходит много ошибок. Я также держу несколько модульных тестов, которые работают с базой данных (ради использования TDD или быстрого локального тестирования), но эти тесты никогда не выполняются на моем сервере непрерывной интеграции, так как мы не храним базу данных для этой цели, и я думаю, что тесты, которые выполняются на сервере CI, должны быть автономными.
Другой подход, который я нахожу очень интересным, но не всегда стоящим, поскольку требует немного времени, заключается в создании той же схемы, которую вы используете для производства, во встроенной базе данных, которая просто выполняется в рамках модульного тестирования.
Несмотря на то, что нет сомнений в том, что этот подход улучшает ваше покрытие, у него есть несколько недостатков, поскольку вы должны быть как можно ближе к ANSI SQL, чтобы он работал как с вашей текущей СУБД, так и со встроенной заменой.
Неважно, что вы считаете более подходящим для вашего кода, есть несколько проектов, которые могут упростить его, например, DbUnit .
источник
Даже если есть инструменты , которые позволяют издеваться вашу базу данных в той или иной форме (например , jOOQ «ами
MockConnection
, которые можно увидеть в этом ответе - оговорке, я работаю продавцом jOOQ в), я бы посоветовал не издеваться больших баз данных со сложной запросы.Даже если вы просто хотите провести интеграционное тестирование своего ORM, имейте в виду, что ORM выдает очень сложную серию запросов к вашей базе данных, которые могут различаться
Подделать все это, чтобы получить разумные фиктивные данные, довольно сложно, если только вы на самом деле не создаете небольшую базу данных внутри вашего макета, которая интерпретирует передаваемые операторы SQL. При этом используйте хорошо известную базу данных интеграционных тестов, которую вы легко можете сбросить с хорошо известными данными, для которой вы можете запустить интеграционные тесты.
источник
Я использую первый (запуск кода для тестовой базы данных). Единственная существенная проблема, которую я вижу, когда вы поднимаете этот подход, - это возможность несинхронизации схем, с которой я имею дело, сохраняя номер версии в моей базе данных и внося все изменения схемы с помощью скрипта, который применяет изменения для каждого приращения версии.
Я также сначала делаю все изменения (в том числе в схеме базы данных) в моей тестовой среде, так что в итоге все получается наоборот: после прохождения всех тестов примените обновления схемы к рабочему хосту. Я также держу отдельную пару тестируемых и прикладных баз данных в моей системе разработки, чтобы там можно было проверить, что обновление базы данных работает должным образом, прежде чем касаться реального производственного блока (ов).
источник
Я использую первый подход, но немного другой, который позволяет решать упомянутые проблемы.
Все, что необходимо для запуска тестов для DAO, находится в системе контроля версий. Он включает в себя схему и сценарии для создания БД (докер очень хорош для этого). Если можно использовать встроенную БД - я использую ее для скорости.
Важное отличие от других описанных подходов состоит в том, что данные, необходимые для тестирования, не загружаются из сценариев SQL или файлов XML. Все (кроме некоторых словарных данных, которые фактически являются постоянными) создается приложением с использованием служебных функций / классов.
Основная цель - сделать данные, используемые тестом
По сути, это означает, что эти утилиты позволяют декларативно указывать только те вещи, которые необходимы для теста, в самом тесте и опускать ненужные вещи.
Чтобы дать некоторое представление о том, что это означает на практике, рассмотрим тест для некоторого DAO, который работает с
Comment
s кPost
s, написаннымAuthors
. Чтобы протестировать CRUD-операции для таких DAO, в БД должны быть созданы некоторые данные. Тест будет выглядеть так:Это имеет несколько преимуществ перед сценариями SQL или файлами XML с тестовыми данными:
Откат против коммитов
Я считаю более удобным, что тесты фиксируются, когда они выполняются. Во-первых, некоторые эффекты (например,
DEFERRED CONSTRAINTS
) не могут быть проверены, если коммит никогда не происходит. Во-вторых, если тест не пройден, данные могут быть проверены в БД, поскольку они не возвращаются при откате.Конечно, у этого есть недостаток, что тест может дать неверные данные, что приведет к сбоям в других тестах. Чтобы справиться с этим, я пытаюсь выделить тесты. В приведенном выше примере каждый тест может создавать новые,
Author
и все другие объекты, связанные с ним, создаются, поэтому столкновения случаются редко. Чтобы справиться с оставшимися инвариантами, которые могут быть потенциально нарушены, но не могут быть выражены как ограничение уровня БД, я использую некоторые программные проверки для ошибочных условий, которые могут выполняться после каждого отдельного теста (и они запускаются в CI, но обычно отключаются локально для производительности причины).источник
PostBuilder.post()
. Он генерирует некоторые значения для всех обязательных атрибутов поста. Это не нужно в производственном коде.Для проекта на основе JDBC (прямо или косвенно, например, JPA, EJB, ...) вы можете макетировать не всю базу данных (в таком случае было бы лучше использовать тестовую базу данных на реальной СУБД), но только макет на уровне JDBC ,
Преимущество - это абстракция, которая идет таким образом, поскольку данные JDBC (набор результатов, количество обновлений, предупреждение и т. Д.) Одинаковы независимо от того, что является бэкэндом: ваша prod db, test db или просто некоторые данные макета, предоставленные для каждого теста кейс.
При макетировании соединения JDBC для каждого случая нет необходимости управлять тестовой БД (очистка, только один тест за раз, перезагрузка приборов, ...). Каждое соединение макета изолировано, и нет необходимости в очистке. В каждом тестовом примере предусмотрены только минимальные необходимые приспособления для макетирования обмена JDBC, что помогает избежать сложности управления всей тестовой базой данных.
Acolyte - это мой фреймворк, который включает драйвер JDBC и утилиту для такого макета: http://acolyte.eu.org .
источник