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

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

В настоящий момент у нас есть единственный сервер базы данных с живой схемой и схемой _test. Когда тест запускается, он сначала запускает SQL script, в котором говорится, как заполнять тестовую базу данных (и удалять любые старые данные, которые могут мешать). Это происходит практически для всех тестов. Из того, что я вижу, это самое большое узкое место в наших тестах - я только что профилировал один тест, и для настройки базы данных требуется около 800 мс, а затем каждый последующий тест проходит примерно через 10 мс.

Я пытался найти некоторые решения, и вот что я нашел до сих пор:

  • У вас есть одна тестовая схема, и изменения отката в конце каждого теста.

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

  • Откажитесь от базы данных, где это возможно

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

  • Запустите ту же систему, но используйте дампы и RAMFS

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

    Я не могу проверить это, потому что я на OSX и из того, что я вижу, не поддерживает ramfs.

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

Halp!:)

Ответы

Ответ 1

В Эффективно работает с устаревшим кодом, пишет Michael Feathers (стр. 10)

Тестирование устройств выполняется быстро. Если они не работают быстро, они не являются модульными тестами.

Другие виды тестов часто маскируются как модульные тесты. Тест не является unit test, если:

  • Он ведет переговоры с базой данных.
  • Он осуществляет связь по сети.
  • Он касается файловой системы.
  • Чтобы запустить его, вам необходимо сделать что-то особенное для вашей среды (например, для редактирования файлов конфигурации).

Тесты, которые делают эти вещи, неплохие. Часто их стоит писать, и вы обычно записываете их в жгуты unit test. Тем не менее, важно иметь возможность отделить их от настоящих модульных тестов, чтобы вы могли поддерживать набор тестов, которые можно быстро запускать, когда вы вносите изменения.

Если вы не проводите быстрые тесты устройств, они теряют ценность, потому что разработчики не будут запускать их все время. Чтобы быть конкретным, Feathers определяет медленный unit test как тот, который занимает одну десятую секунды или больше для выполнения.

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

Не откладывайте свои интеграционные тесты и забывайте о них! Автоматизируйте их выполнение и отчетность о результатах. Если вы используете сервер непрерывной интеграции, добавьте еще один проект, который ничего не делает, кроме как периодически запускает тесты.

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

Ответ 2

Интересный вопрос. По звукам вещей, которые вы пытаетесь делать модульные тесты против базы данных, это плохая идея. Вы хотите, чтобы эти тесты были как можно быстрее. Если вы используете слой данных, тогда вы можете подумать об издевательствах, чтобы он работал в памяти. Протестируйте макет datalayer.

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

Edit

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

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

Изменить второй

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