【问题标题】:Random error when testing with NHibernate on an in-Memory SQLite db在内存 SQLite 数据库上使用 NHibernate 进行测试时出现随机错误
【发布时间】:2011-08-05 11:48:37
【问题描述】:

我有一个系统,它在收到消息后将其入队(写入表),另一个进程轮询数据库并将其出列以进行处理。在我的自动测试中,我已将操作合并到同一进程中,但不能(从概念上)合并来自这两个操作的 NH 会话。

自然会出现问题。

我已经阅读了有关让 SQLite-InMemory-NHibernate 组合在测试世界中工作的所有内容,但由于“没有这样的表”错误,我现在遇到了随机失败的测试。明确一点 - “随机”意味着使用相同的配置和代码进行相同的测试有时会失败。

我有以下 SQLite 配置:

return SQLiteConfiguration
 .Standard
 .ConnectionString(x => x.Is("Data Source=:memory:; Version=3; New=True; Pooling=True; Max Pool Size=1;"))
 .Raw(NHibernate.Cfg.Environment.ReleaseConnections, "on_close");

在我的测试开始时(每次测试),我都会获取“静态”会话提供程序,并请求它刷新现有的数据库并重新创建架构:

public void PurgeDatabaseOrCreateNew()
{
    using (var session = GetNewSession())
    using (var tx = session.BeginTransaction())
    {
            PurgeDatabaseOrCreateNew(session);
            tx.Commit();
    }
}

private void PurgeDatabaseOrCreateNew(ISession session)
{
    //http://ayende.com/Blog/archive/2009/04/28/nhibernate-unit-testing.aspx
    new SchemaExport(_Configuration)
        .Execute(false, true, false, session.Connection, null);
}

所以是的,它在另一个会话上,但连接在 SQLite 上汇集,所以我创建的下一个会话将看到生成的架构。然而,虽然它在大多数情况下都有效 - 有时后来的“入队”操作会失败,因为它看不到我传入消息的表。 此外 - 这似乎在每个测试套件运行最多发生一到两次;并不是所有的测试都失败了,只有第一个(有时还有另一个。不太确定是不是第二个)。

最糟糕的部分自然是随机性。我已经告诉自己我已经修复了好几次了,只是因为它只是“停止了失败”。随意。

这发生在 FW4.0、System.Data.SQLite x86 版本、Win7 64b 和 2008R2(总共三台不同的机器)、NH2.1.2、配置了 FNH、TestDriven.NET 32b 进程和 NUnit 控制台 32b 进程上。

帮助?

【问题讨论】:

  • 我也有这个问题。几乎每个测试都通过了,但偶尔会有一两个测试因“没有这样的表”错误而失败,如果我再次运行它们,它们就会通过。我认为只是 SQLite 在连接池中随机地重新创建连接。

标签: nhibernate testing system.data.sqlite in-memory


【解决方案1】:

您好,我很确定我遇到了与您完全相同的问题。我在每个集成测试中打开和关闭多个会话。在挖掘了 SQLite 连接池和我自己的一些实验之后,我得出了以下结论:

SQLite 池代码使用 Wea​​kReferences 缓存连接,这不是缓存的最佳option,因为当没有正常(强)引用连接时,对连接的引用将被清除,并且GC 运行。由于您无法预测 GC 何时运行,这解释了“随机性”。尝试在关闭一个会话和打开另一个会话之间添加一个GC.Collect();,您的测试将始终失败。

我的解决方案是自己在打开会话之间缓存连接,如下所示:

public class BaseIntegrationTest
{
    private static ISessionFactory _sessionFactory;
    private static Configuration _configuration;
    private static SchemaExport _schemaExport;

    // I cache the whole session because I don't want it and the
    // underlying connection to get closed.
    // The "Connection" property of the ISession is what we actually want.
    // Using the NHibernate SQLite Driver to get the connection would probably
    // work too.
    private static ISession _keepConnectionAlive;

    static BaseIntegrationTest()
    {
        _configuration = new Configuration();
        _configuration.Configure();
        _configuration.AddAssembly(typeof(Product).Assembly);
        _sessionFactory = _configuration.BuildSessionFactory();
        _schemaExport = new SchemaExport(_configuration);

        _keepConnectionAlive = _sessionFactory.OpenSession();
    }

    [SetUp]
    protected void RecreateDB()
    {
        _schemaExport.Execute(false, true, false, _keepConnectionAlive.Connection, null);
    }

    protected ISession OpenSession()
    {
        return _sessionFactory.OpenSession(_keepConnectionAlive.Connection);
    }
}

我的每个集成测试都继承自此类,并调用 OpenSession() 来获取会话。由于 [SetUp] 属性,NUnit 在每次测试之前都会调用 RecreateDB。

我希望这对您或遇到此错误的其他人有所帮助。

【讨论】:

  • 更改 SQLite 的缓存机制可能是最干净的解决方案,但我现在还没有信心这样做;-)
  • 这正是我解决它的方法。很抱歉没有早点发布解决方案。
【解决方案2】:

唯一想到的是您在测试后随机打开会话。在打开另一个 ISession 之前,您必须确保关闭任何现有的 ISession。如果您没有使用 using() 语句或手动调用 Dispose(),则会话可能在某处仍处于活动状态,从而导致这些随机异常。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-29
    • 2018-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 1970-01-01
    相关资源
    最近更新 更多