Кому приходилось писать интеграционные тесты с базой данных, тот познал трудности таких тестов. На то есть две основные причины.
Очистка базы данных для каждого теста.
Пересоздание базы данных весьма долгое (по времени выполнения) и трудоёмкое занятие. С годами выработались основные подходы к решению этой проблемы:
Скорость выполнения тестов.
С проблемой скорости дело обстоит сложнее. Межпроцессные вызовы всегда длятся на несколько порядков медленнее внутрипроцессных, сервера БД склонны записывать состояние БД в файл, что тоже отнимает время. И это серьёзные трудности на пути к эффективному тестированию работы с БД. К счастью, для пользователей NHibernate есть решение проблемы - SQLite in-memory. Давайте попробуем. Вот пример простого теста с использование in-memory SQLite.
Выглядит несложно, но... не работает. Падает с ошибкой "no such table HighLowGenerator". Хотя в скрипте есть dml-запрос на создание таблицы. Сначала вопрос куда делась "such table" может вводить в ступор. Дело в том, что схема БД (и все данные в ней) существует в памяти пока открыто соединение. Как только соединение закрыто - всё, все данные и схема уничтожены. Это происходит в и нашем случае. SchemaExport честно создаёт схему БД в памяти, но после этого закрывает соединение. Сессия работает с новым открытым соединением, в котором нет ничего, даже таблиц. Вот и получаем no such table. Можно ли помочь беде!? Конечно! :)
Сессия NHibernate не открывает и не закрывает внешние соединения, переданные ей извне (т.е. в метод ISessionFactory.OpenSession()). Достаточно лишь немного исправить код:
Конфигурация im-memory примерно на порядок быстрее, чем виртульный диск, и на три порядка быстрее настоящего диска. Так что у пользователей NHibernate нет повода не использовать SQLite для тестирования. :)
Теоретические основы тестирования в связке NHibernate + SQLite я изложил. В следующий раз расскажу, как правильно организовать классы для тестирования с SQLite in-memory.
Очистка базы данных для каждого теста.
Пересоздание базы данных весьма долгое (по времени выполнения) и трудоёмкое занятие. С годами выработались основные подходы к решению этой проблемы:
- Создавать базу из эталонной.
- Откатывать транзакцию по завершении тестов.
- Ломать/Создавать (Drop/Create) схему БД перед каждым тестом.
Скорость выполнения тестов.
С проблемой скорости дело обстоит сложнее. Межпроцессные вызовы всегда длятся на несколько порядков медленнее внутрипроцессных, сервера БД склонны записывать состояние БД в файл, что тоже отнимает время. И это серьёзные трудности на пути к эффективному тестированию работы с БД. К счастью, для пользователей NHibernate есть решение проблемы - SQLite in-memory. Давайте попробуем. Вот пример простого теста с использование in-memory SQLite.
[Test] public void In_memory_fails_on_no_such_table() { var config = new Configuration(); config .ConfigureSQLiteInMemory() .MapEntities(From.ThisApplication()); var factory = config.BuildSessionFactory(); var schemaExport = new SchemaExport(config); schemaExport.Create(true, true); using(var session = factory.OpenSession()) using (var tx = session.BeginTransaction()) { var product = new Product {Name = "Supercomputer"}; session.Persist(product); tx.Commit(); } }
Выглядит несложно, но... не работает. Падает с ошибкой "no such table HighLowGenerator". Хотя в скрипте есть dml-запрос на создание таблицы. Сначала вопрос куда делась "such table" может вводить в ступор. Дело в том, что схема БД (и все данные в ней) существует в памяти пока открыто соединение. Как только соединение закрыто - всё, все данные и схема уничтожены. Это происходит в и нашем случае. SchemaExport честно создаёт схему БД в памяти, но после этого закрывает соединение. Сессия работает с новым открытым соединением, в котором нет ничего, даже таблиц. Вот и получаем no such table. Можно ли помочь беде!? Конечно! :)
Сессия NHibernate не открывает и не закрывает внешние соединения, переданные ей извне (т.е. в метод ISessionFactory.OpenSession()). Достаточно лишь немного исправить код:
[Test] public void In_memory_succeeded() { var config = new Configuration(); config .ConfigurSQLiteInMemory() .MapEntities(From.ThisApplication()); var factory = config.BuildSessionFactory(); var connection = new SQLiteConnection("Data Source=:memory:;Version=3;New=True;"); connection.Open(); var schemaExport = new SchemaExport(config); schemaExport.Execute(true, true, false, connection, null); using(var session = factory.OpenSession(connection)) using (var tx = session.BeginTransaction()) { var product = new Product {Name = "Supercomputer"}; session.Persist(product); session.Flush(); tx.Commit(); } }Это работает, и быстро, практически мгновенно! Ниже в таблице сравнительный тест разных конфигураций SQLite (среднее за 100 прогонов на создание/удаление схемы БД для тестового проекта Enhima). "Анализ производительности" не претендует на точность, но представление о времени работы даёт.
In memory | Virtual drive | Real drive | |
Экспорт схемы, мс | 2 | 19 | 1053 |
Полное время теста, мс | 246 | 1929 | 105246 |
Способ очистки БД | Закрытие соединения | Удаление файла | Удаление файла |
Конфигурация im-memory примерно на порядок быстрее, чем виртульный диск, и на три порядка быстрее настоящего диска. Так что у пользователей NHibernate нет повода не использовать SQLite для тестирования. :)
Теоретические основы тестирования в связке NHibernate + SQLite я изложил. В следующий раз расскажу, как правильно организовать классы для тестирования с SQLite in-memory.
Комментариев нет:
Отправить комментарий