Использование [Rollback] MbUnit3 для модульного тестирования взаимодействий NHibernate с SQLite - PullRequest
2 голосов
/ 27 марта 2009

Справочная информация:

Моя команда стремится к тому, чтобы прямо с кассы наши компиляции кода и модульные тесты выполнялись успешно. Чтобы упростить это и протестировать некоторые из наших сопоставлений 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 ]

2 голосов
/ 27 марта 2009

Это не реальный ответ на ваш вопрос, но, вероятно, решение проблемы.

Я использую реализацию sql lite в памяти для своих интеграционных тестов. Я строю схему и заполняю базу перед каждым тестом. Создание схемы и начальное заполнение данных происходит очень быстро (менее 0,01 секунды на тест), потому что это база данных в памяти.

Почему вы используете физическую базу данных?

Изменить: ответ на ответ на вопрос выше:

1.) Поскольку я перенес свою схему и данные непосредственно из SQL Server 2005, и я хочу, чтобы она сохранялась в системе контроля версий.

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

2.) Отличается ли что-то в механизме SQLite в памяти таким образом, что это решило бы эту проблему?

  • Это может решить ваши проблемы, потому что вы можете воссоздавать свою базу данных перед каждым тестом. Тестируемая база данных будет в том состоянии, в котором вы ожидаете ее до выполнения каждого теста. Преимущество этого заключается в том, что нет необходимости откатывать ваши транзакции, но я запустил аналогичный тест в памяти sqllite, и он работал в соответствии с требованиями.
1 голос
/ 28 марта 2009

Проверьте, не пропускаете ли вы connection.release_mode=on_close в конфигурации SQLite NHibernate. ( справочные документы )

Кстати: всегда утилизируйте свои ISession и ISessionFactory.

0 голосов
/ 27 марта 2009

Ditch [Rollback] и используйте NDbUnit . Я сам использую это для этого точного сценария, и он работал отлично.

...