MbUnit3 の [Rollback] を使用した単体テスト NHibernate と SQLite の相互作用
-
22-08-2019 - |
質問
背景:
私のチームは、チェックアウト直後からコードのコンパイルと単体テストが正常に実行されることを保証することに専念しています。これを容易にし、NHibernate マッピングの一部をテストするために、実稼働 SQL Server 2005 データベースのミラーである SQLite DB をリポジトリに追加しました。以下の最新バージョンを使用しています。MbUnit3 (Gallio の一部)、System.Data.SQLite、および NHibernate。
問題:
次の単体テストは、SQL Server 2005 に対しては問題なく実行できましたが、SQLite では機能しないことがわかりました。
[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] 属性を指定しない場合もテストは正常に完了します。
質問:
これは、MbUnit3 が [ロールバック] に使用する System.Data.SQLite の TransactionScope 実装の問題ですか、それとも SQLite エンジンの制限ですか?
テストが実行されるたびにデータベースに影響を与えないようにロールバックする、SQLite に対して動作するこの単体テストを作成する方法はありますか?
解決
抜けていないか確認してください 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 で同様のテストを実行したところ、問題なく動作しました。
[ロールバック]を捨てて使用してください NDbユニット. 。私自身もこのシナリオでこれを使用していますが、非常にうまく機能しています。