Фон:
Моя команда стремится обеспечить успешное выполнение компиляции кода и модульных тестов сразу после оформления заказа. Чтобы облегчить это и протестировать некоторые из наших сопоставлений NHibernate, мы добавили базу данных SQLite в наш репозиторий, которая является зеркалом нашей производственной базы данных SQL Server 2005. Мы используем последние версии: MbUnit3 (часть Gallio), System.Data.SQLite и NHibernate.
Проблема:
Я обнаружил, что следующий модульный тест не работает с SQLite, несмотря на то, что он выполняется без проблем с SQL Server 2005.
[Test]
[Rollback]
public void CompleteCanPersistNode()
{
// returns a Configuration for either SQLite or SQL Server 2005 depending on how the project is configured.
Configuration config = GetDbConfig();
ISessionFactory sessionFactory = config.BuildSessionFactory();
ISession session = sessionFactory.OpenSession();
Node node = new Node();
node.Name = "Test Node";
node.PhysicalNodeType = session.Get<NodeType>(1);
// SQLite fails with the exception below after the next line called.
node.NodeLocation = session.Get<NodeLocation>(2);
session.Save(node);
session.Flush();
Assert.AreNotEqual(-1, node.NodeID);
Assert.IsNotNull(session.Get<Node>(node.NodeID));
}
Исключение, которое я получаю (ТОЛЬКО при работе с SQLite), следующее:
NHibernate.ADOException: cannot open connection ---> System.Data.SQLite.SQLiteException: The database file is locked database is locked at System.Data.SQLite.SQLite3.Step(SQLiteStatement stmt) at System.Data.SQLite.SQLiteDataReader.NextResult() at System.Data.SQLite.SQLiteDataReader..ctor(SQLiteCommand cmd, CommandBehavior behave) at System.Data.SQLite.SQLiteCommand.ExecuteReader(CommandBehavior behavior) at System.Data.SQLite.SQLiteCommand.ExecuteNonQuery() at System.Data.SQLite.SQLiteTransaction..ctor(SQLiteConnection connection, Boolean deferredLock) at System.Data.SQLite.SQLiteConnection.BeginDbTransaction(IsolationLevel isolationLevel) at System.Data.SQLite.SQLiteConnection.BeginTransaction() at System.Data.SQLite.SQLiteEnlistment..ctor(SQLiteConnection cnn, Transaction scope) at System.Data.SQLite.SQLiteConnection.EnlistTransaction(Transaction transaction) at System.Data.SQLite.SQLiteConnection.Open() at NHibernate.Connection.DriverConnectionProvider.GetConnection() at NHibernate.Impl.SessionFactoryImpl.OpenConnection() --- End of inner exception stack trace --- at NHibernate.Impl.SessionFactoryImpl.OpenConnection() at NHibernate.AdoNet.ConnectionManager.GetConnection() at NHibernate.AdoNet.AbstractBatcher.Prepare(IDbCommand cmd) at NHibernate.AdoNet.AbstractBatcher.ExecuteReader(IDbCommand cmd) at NHibernate.Loader.Loader.GetResultSet(IDbCommand st, Boolean autoDiscoverTypes, Boolean callable, RowSelection selection, ISessionImplementor session) at NHibernate.Loader.Loader.DoQuery(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) at NHibernate.Loader.Loader.DoQueryAndInitializeNonLazyCollections(ISessionImplementor session, QueryParameters queryParameters, Boolean returnProxies) at NHibernate.Loader.Loader.LoadEntity(ISessionImplementor session, Object id, IType identifierType, Object optionalObject, String optionalEntityName, Object optionalIdentifier, IEntityPersister persister) at NHibernate.Loader.Entity.AbstractEntityLoader.Load(ISessionImplementor session, Object id, Object optionalObject, Object optionalId) at NHibernate.Loader.Entity.AbstractEntityLoader.Load(Object id, Object optionalObject, ISessionImplementor session) at NHibernate.Persister.Entity.AbstractEntityPersister.Load(Object id, Object optionalObject, LockMode lockMode, ISessionImplementor session) at NHibernate.Event.Default.DefaultLoadEventListener.LoadFromDatasource(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) at NHibernate.Event.Default.DefaultLoadEventListener.DoLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) at NHibernate.Event.Default.DefaultLoadEventListener.Load(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) at NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options) at NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType) at NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType) at NHibernate.Impl.SessionImpl.Get(String entityName, Object id) at NHibernate.Impl.SessionImpl.Get(Type entityClass, Object id) at NHibernate.Impl.SessionImpl.Get[T](Object id) D:\dev\598\Code\test\unit\DataAccess.Test\NHibernatePersistenceTests.cs
Когда используется SQLite и НЕ указан атрибут [Rollback], тест также завершается успешно.
Вопрос:
Это проблема с реализацией TransactionScope в System.Data.SQLite, которую использует MbUnit3 для [отката], или с ограничением механизма SQLite?
Есть ли способ написать этот модульный тест, работающий против SQLite, который будет выполнять откат, чтобы не влиять на базу данных при каждом запуске теста?
3 ответа
Убедитесь, что вы не пропустили connection.release_mode=on_close
в конфигурации SQLite NHibernate. (справочные документы)
Кстати: всегда выбрасывайте свои ISession
и ISessionFactory
.
Это не настоящий ответ на ваш вопрос, но, вероятно, решение проблемы.
Я использую реализацию sql lite в памяти для своих интеграционных тестов. Я создаю схему и заполняю базу данных перед каждым тестом. Создание схемы и начальное заполнение данных происходит очень быстро (менее 0,01 секунды на тест), потому что это база данных в памяти.
Почему вы используете физическую базу данных?
Изменить: ответ на ответ на вопрос выше:
1.) Потому что я перенес свою схему и данные непосредственно из SQL Server 2005, и я хочу, чтобы они сохранялись в системе управления версиями.
- Я рекомендую сохранить файл со схемой базы данных, а также файл или сценарий, который создает образцы данных в системе управления версиями. Вы можете сгенерировать файл с помощью sql server studion management express, вы можете сгенерировать его из ваших сопоставлений NHibernate или вы можете использовать такой инструмент, как sql compare, и вы, вероятно, сможете найти другие решения для этого, когда вам это нужно. Простые текстовые файлы легче хранить в системах контроля версий, чем полные двоичные файлы базы данных.
2.) Отличается ли что-то в движке SQLite в памяти таким образом, чтобы решить эту проблему?
- Это может решить ваши проблемы, потому что вы можете воссоздавать свою базу данных перед каждым тестом. Ваша тестируемая база данных будет находиться в ожидаемом состоянии перед выполнением каждого теста. Преимущество этого заключается в том, что нет необходимости откатывать ваши транзакции, но я провел аналогичный тест с sqllite в памяти, и он работал должным образом.
Откажитесь от [отката] и используйте NDbUnit. Я сам использую это для этого точного сценария, и он отлично работает.
Похожие вопросы
Новые вопросы
unit-testing
Модульное тестирование - это метод, с помощью которого отдельные блоки исходного кода тестируются, чтобы определить, пригодны ли они для использования.