通过 nhibernate 对数据库查询进行单元测试

Posted

技术标签:

【中文标题】通过 nhibernate 对数据库查询进行单元测试【英文标题】:unit testing a database query via nhibernate 【发布时间】:2012-08-30 12:50:01 【问题描述】:

我需要为从数据库加载数据的方法创建一个单元测试。我对单元测试和数据库进行了一些研究,大多数文章都告诉你应该模拟数据库。但是,这种方法基本上是从数据库中加载对象,并通过 SQL 执行一些限制。

因此,我要测试的是实际的数据库查询是否成功,因此我认为无法模拟数据库。

我使用 NHibernate 作为我的 ORM,并且正在使用 QueryOver 构建查询。由于数据库处于不一致的状态,我发现对单元测试数据库非常有问题。任何想法/方法将如何进行这样的测试?

这是我想要进行单元测试的特定方法:

public IEnumerable<IArticlePanel> LoadPanelsApplicableToArticle(ArticleModule.IArticle article, Enums.ARTICLE_PANEL_LOCATION location)
   
       CS.General_v3.Util.ContractsUtil.RequiresNotNullable(article, "Article must not be null");

       var articleList = Modules.Factories.ArticleFactory.GetAllParentsForAnArticle(article).ToList();
       articleList.Add(article);

       var q = GetQuery();
       q = q.WhereRestrictionOn(x => x.Article).IsInG(articleList.ConvertAll<long>(x => ((IBaseDbObject)x).ID));
       q = q.Where(x => x.Location == location);
       return FindAll(q);
 

【问题讨论】:

切换到sqlite数据库进行测试。 【参考方案1】:

过去,当我需要对数据库进行单元测试时,我通常使用SQLite。您基本上在内存中设置 SQLite 数据库,然后配置您的 NHibernate(依赖注入,或者您想这样做)以连接到 SQLite 而不是您的普通数据库。几乎所有查询都应该能够正确运行。

如果您需要强大的 DateTime 支持,SQLite 可能会让您失望(请参阅 Ayende 关于 here 的帖子)。在这种情况下,您可以使用任何嵌入式数据库。我建议设置一个 RAMDisk 并将嵌入式数据库放在该磁盘上,这样它仍然可以在内存中运行。

【讨论】:

这种方法的问题是有一些设置,而其他值必须在数据库中作为某些初始化代码等取决于它们。因此,我需要从头开始重新创建 SQLite,填充这些设置等。我尝试过这条路线,但有时它会变得非常难以维护。 如果您处于对数据库本身进行单元测试实际上很关键的情况下,那么投入一点额外时间来编写 SQLite 数据库创建和自定义代码通常不是什么大问题。我从来没有遇到过需要超过一天的时间来编写初始化数据库所需的所有内容,然后为所有未来的单元测试完成的情况。如果不值得花那么多时间,那么可能根本不值得担心数据层的单元测试。【参考方案2】:

我个人使用真实数据库进行集成测试。我觉得这是尽可能接近真实的生产场景。我们的开发团队目前没有从 nhibernate 映射生成数据库,因此映射和数据库之间存在一些不一致(例如数据库默认值等)。如果您从 nhibernate SQLLite 生成模式可能是您的正确路径。但如果你不是,我个人认为最好针对真实数据库编写这些测试。

我的集成测试将插入测试所需的数据并在测试后删除数据。此方法的唯一缺陷是您必须确保在测试结束后删除所有数据,否则可能会影响其他测试。不过,我发现这对我们公司来说是一个可行的解决方案,而且非常有帮助。我们有一个专门用于单元测试的数据库。

以下是我的一项测试的示例:

[TestMethod]
public void Test_NHibernate_Query()

    //Create the data in the database necessary to test my nhibernate query
    CreateDataForUnitTest();

    IInventoryRepository target = new InventoryRepository(nhibernateSession);

    IList<InventoryView> inventoryRecords = target.GetContainerInventory(productId);

    Assert.AreEqual(1, inventoryRecords.Count);


[TestCleanup]
public void CleanUp()

    DeleteAll<Order>();
    DeleteAll<Company>();


public void DeleteAll<T>() where T : Entity

    NHibernate.ISession session = SessionFactory.GetCurrentSession();

    using (NHibernate.ITransaction tran = session.BeginTransaction())
    
        IList<T> items = session.CreateCriteria<T>()
            .List<T>();

        foreach (T p in items)
        
            session.Delete(p);
        

        tran.Commit();
    

【讨论】:

这就是我目前的工作方式。但是,有时由于依赖关系,在之后清理并将数据库恢复到一致状态并不是那么容易。我想更多地看看是否有我目前不知道的选项,但是这似乎是我的情况的最佳方式。 如果您使用的是 mssql,如果一致状态意味着空,则可能有一种简单的方法可以使数据库恢复到一致状态。 ***.com/questions/155246/… 我实际上可能会考虑这样做。【参考方案3】:

我们使用 NCommon 的 IRepository。然后我们使用 InMemoryRepository 进行单元测试。非常流畅、快速且易于使用。

【讨论】:

以上是关于通过 nhibernate 对数据库查询进行单元测试的主要内容,如果未能解决你的问题,请参考以下文章

使用内存 sql lite 对流利的 nhibernate 存储库进行单元测试 - 没有这样的表错误

优化 NHibernate 查询

ORM篇——有关NHibernate查询封装

Nhibernate系列学习之 Criteria查询表达式增删改查

我应该对数据访问层进行单元测试吗?这是一个好习惯以及如何去做?

NHibernate 数字类型进行模糊查询