2

永続化のためにCastle ActiveRecordを使用しています。次のことを行う永続化テストの基本クラスを作成しようとしています:

  • テスト ケースごとにトランザクションを開き、テスト ケースの最後にロールバックして、テスト ケースごとにスキーマを再構築しなくても、テスト ケースごとにクリーンな DB を取得できるようにします。
  • NHibernate セッションをフラッシュし、テストの途中で新しいセッションを取得する機能を提供して、永続化操作が NHibernate セッションだけでなく DB に実際にヒットしたことを確認できるようにします。

基本クラス ( ) が機能していることを証明するためにARTestBase、次のサンプル テストを作成しました。

[TestFixture]
public class ARTestBaseTest : ARTestBase
{
    [Test]
    public void object_created_in_this_test_should_not_get_committed_to_db()
    {
        ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});

        Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
    }

    [Test]
    public void object_created_in_previous_test_should_not_have_been_committed_to_db()
    {
        ActiveRecordMediator<Entity>.Save(new Entity {Name = "test"});

        Assert.That(ActiveRecordMediator<Entity>.Count(), Is.EqualTo(1));
    }

    [Test]
    public void calling_flush_should_make_nhibernate_retrieve_fresh_objects()
    {
        var savedEntity = new Entity {Name = "test"};
        ActiveRecordMediator<Entity>.Save(savedEntity);
        Flush();
        // Could use FindOne, but then this test would fail if the transactions aren't being rolled back
        foreach (var entity in ActiveRecordMediator<Entity>.FindAll())
        {
            Assert.That(entity, Is.Not.SameAs(savedEntity));
        }
    }
}

これが基本クラスでの私の最善の努力です。を正しく実装Flush()しているため、3 番目のテスト ケースはパスします。ただし、トランザクションはロールバックされないため、2 番目のテストは失敗します。

public class ARTestBase
{
    private SessionScope sessionScope;
    private TransactionScope transactionScope;

    [TestFixtureSetUp]
    public void InitialiseAR()
    {
        ActiveRecordStarter.ResetInitializationFlag();
        ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
        ActiveRecordStarter.CreateSchema();
    }

    [SetUp]
    public virtual void SetUp()
    {
        transactionScope = new TransactionScope(OnDispose.Rollback);
        sessionScope = new SessionScope();
    }

    [TearDown]
    public virtual void TearDown()
    {
        sessionScope.Dispose();
        transactionScope.Dispose();
    }

    protected void Flush()
    {
        sessionScope.Dispose();
        sessionScope = new SessionScope();
    }

    [TestFixtureTearDown]
    public virtual void TestFixtureTearDown()
    {
        SQLiteProvider.ExplicitlyDestroyConnection();
    }
}

インメモリ データベースでカスタム SQLite プロバイダーを使用していることに注意してください。このブログ投稿から引用した私のカスタム プロバイダーは、スキーマを維持するために常に接続を開いたままにします。これを削除して通常の SQL Server データベースを使用しても、動作は変わりません。

必要な動作を実現する方法はありますか?

4

2 に答える 2

1

ActiveRecord についてはよくわかりませんが、NHibernate ではトランザクションはセッションに属し、その逆ではありません。

IDbTransactionADO.Net をよく使用している場合は、接続を使用するために必要な を作成するため、これはより理にかなっています。ActiveRecord TransactionScope(および NHibnerate ITransaction) は基本的に をラップするため、 の前にIDbTransactionを作成する必要があります。SessionScopeTransactionScope

また、(NHibernate 1.2 GA または NHibernate 2.* を使用しているかどうか、および何FlushModeを持っているかに応じて) を見つける可能性があるのは、NHibernate が取得できないことに気付くため、へのSessionScope呼び出しによってセッションがフラッシュされる可能性があることです。FindAll()への最後の呼び出しを実行せずに正しいデータSave

これですべて完了しました。SessionScope.Flush()新しい を作成する代わりに を使用してみましたSessionScopeか?

于 2008-11-03T10:49:47.917 に答える
0

を使用SessionScope.Flush()すると、3 番目のテストが失敗します。私が理解しているようにFlush()、SQL を実行してレコードを DB にプッシュしますが、セッションからオブジェクトを削除しません。FindAll()それは、フラッシュを引き起こすことについてあなたが言うことと一致します.

私が本当に欲しいのはSessionScope.Flush()、(DB の状態をセッションと同期するため) プラスSessionScope.EvictAll()(後続のクエリで新しいオブジェクトを確実に取得するため) です。私new SessionScope()はシミュレートする試みでしたEvictAll()

その逆ではなく、トランザクションを囲むセッションについてのあなたのコメントは、私にアイ​​デアを与えてくれました. フラッシュされSessionScopeた内部で新しい内部 を作成し、それがトランザクションに参加することを期待することがどれほどコーシャであるかはわかりませんが、うまくいくようです:TransactionScopeSessionScope

public abstract class ARTestBase
{
    private SessionScope sessionScope;
    private TransactionScope transactionScope;
    private bool reverse;
    private IList<SessionScope> undisposedScopes;

    [TestFixtureSetUp]
    public void InitialiseAR()
    {
        ActiveRecordStarter.ResetInitializationFlag();
        ActiveRecordStarter.Initialize(typeof (Entity).Assembly, ActiveRecordSectionHandler.Instance);
        ActiveRecordStarter.CreateSchema();
        InitialiseIoC();
        undisposedScopes = new List<SessionScope>();
    }

    [SetUp]
    public virtual void SetUp()
    {
        sessionScope = new SessionScope();
        transactionScope = new TransactionScope(OnDispose.Rollback);
        transactionScope.VoteRollBack();
        base.CreateInstanceUnderTest();
        reverse = false;
    }

    [TearDown]
    public virtual void TearDown()
    {
        if (reverse)
        {
            sessionScope.Dispose();
            transactionScope.Dispose();
        }
        else
        {
            transactionScope.Dispose();
            sessionScope.Dispose();
        }
    }

    [TestFixtureTearDown]
    public virtual void TestFixtureTearDown()
    {
        foreach (var scope in undisposedScopes)
        {
            scope.Dispose();
        }
        SQLiteProvider.ExplicitlyDestroyConnection();
    }

    protected void Flush()
    {
        reverse = true;
        sessionScope.Flush();
        undisposedScopes.Add(sessionScope);
        sessionScope = new SessionScope();
    }
}

さらに考えてみると、これにより、各テストケースで複数回フラッシュすることはできなくなります。スコープをもっと注意深く追跡することで、それを処理できると思います。後で調べてみようかな。

于 2008-11-03T13:00:56.217 に答える