我正在使用Castle ActiveRecord进行持久化,我正在尝试为持久性测试编写一个基类,它将执行以下操作:
为了证明我的基类(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()
,因此第三个测试用例通过了。但是它不会回滚事务,因此第二次测试失败。
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提供程序。我的自定义提供程序取自this blog post,始终保持连接处于打开状态以维护架构。删除它并使用常规SQL Server数据库不会改变行为。
有没有办法实现所需的行为?
答案 0 :(得分:1)
对ActiveRecord不太确定,但在NHibernate中,一个事务属于一个会话,而不是另一个事务。
如果您经常使用ADO.Net,这将更有意义,因为您需要创建一个IDbTransaction
来使用连接。 ActiveRecord的TransactionScope
(和NHibnerate的ITransaction
)基本上包裹IDbTransaction
,因此您需要在SessionScope
之前创建TransactionScope
。
您可能也会发现(取决于您使用的是NHibernate 1.2 GA或NHibernate 2. *,以及FlushMode
SessionScope
}您对FindAll()
的调用)可能导致会话无论如何都会刷新,因为NHibernate会意识到它无法在不调用Save
的最后一次调用的情况下检索正确的数据。
这一切都说完了,您是否尝试过使用SessionScope.Flush()
而不是创建新的SessionScope
?
答案 1 :(得分:0)
使用SessionScope.Flush()
使我的第三次测试失败。据我了解,Flush()
执行SQL将我的记录推送到数据库中,但不会从会话中逐出对象。这符合您对FindAll()
导致刷新的看法。
我真正想要的是SessionScope.Flush()
(将DB的状态与会话同步)加上SessionScope.EvictAll()
(以确保我在后续查询中获得新鲜对象)。我的new SessionScope()
试图模拟EvictAll()
。
你对围绕交易而不是反过来的会话的评论确实给了我一个想法。我不确定在刷新的SessionScope
内的TransactionScope
内创建一个新的SessionScope
是多么犹豫,并期望它参与交易,但似乎有效:< / p>
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();
}
}
进一步考虑,这不允许您在每个测试用例中多次刷新。我想我可以通过更仔细地跟踪范围来处理这个问题。我可能会稍后调查一下。