【问题标题】:Is it good practice to use Entity Framework in unit tests?在单元测试中使用实体框架是一种好习惯吗?
【发布时间】:2011-01-06 09:34:36
【问题描述】:

我正在为服务编写 NUnit 测试。

我决定通过将 SQL 语句 直接发送到数据库而不是使用 Entity Framework 模型来做到这一点,以便我可以确定 strong> 的结果,即我正在测试数据库中的内容,而不是实体框架告诉我的数据库中的内容(例如,它可能报告缓存结果等)

唯一的缺点是使用 SqlCommand、SqlDataReader 等编写此代码变得乏味,并且能够使用 EF 模型 会非常 >更容易

其他人是怎么做到的?在编写测试时使用实体框架是一种好习惯,还是应该使用直接调用数据库来确保准确的结果?

【问题讨论】:

    标签: .net unit-testing entity-framework


    【解决方案1】:

    我会说在测试服务时这是一个不好的做法,因为它会创建对数据库的依赖。

    我所做的是将我的模型类汇总到存储库模式中。存储库模式被编码为一个接口,以便我可以更改数据库查询的实现而不影响代码的其他部分。它可以很容易地被模拟并用于测试。这是我构建的界面。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace App.Core.Repositories
    {
        public interface IRepository<TKey, TModel>
            where TKey : IComparable
            where TModel : class
        {
            TModel Only(TKey key);
            IQueryable<TModel> Where(Func<TModel, bool> query);
    
            TModel Single(Func<TModel, bool> query);
            TModel First(Func<TModel, bool> query);
            IQueryable<TModel> All();
    
            void Insert(TModel entity);
            void Insert(IEnumerable<TModel> entities);
    
            void Update(TModel entity);
            void Update(IEnumerable<TModel> entities);
    
            void Remove(TModel entities);
            void Remove(Func<TModel, bool> query);
            void Remove(IEnumerable<TModel> entities);
        }
    
        public interface IRepository<TModel> : IRepository<int, TModel> where TModel : class { }
    
        public interface IRepository : IRepository<int, object> { }
    }
    

    【讨论】:

    • 我们也使用存储库,但我们仍然对实现存储库的数据库库进行单元测试。它们是两种不同的东西。
    【解决方案2】:

    我们在单元测试中对Back Door Manipulation 使用 LINQ to SQL,而不是使用 LINQ to Entities,因为它更接近数据库架构。

    但是,我们并没有厌倦做所有的 SqlCommand 等东西,而是厌倦了让自动生成的 L2S 代码与实际数据库保持同步......

    【讨论】:

    • 在另一个应用程序域中使用 EF 上下文来确保您看到持久数据不是绰绰有余吗?尤其是因为 L2SQL 将您与 MSSQL 联系在一起。
    • 与 MSSQL 绑定的观点。但是,您认为有必要在另一个 AppDomain 中启动 EF 吗?我认为不同的 EF 上下文就足够了。那时几乎不会是后门操纵,所以在这一点上,我认为这个练习是多余的。
    【解决方案3】:

    恰恰相反。我们的 DA 层的测试根本不关心数据库。

    例如。我们有一个测试调用 Save 方法来保存一个新的数据对象。然后它调用 Load 方法来检查对象是否可以被加载回来。我不在乎该对象是否实际保存到数据库中,我只关心我可以检索已保存的对象。如果 EF 想要做一些巧妙的缓存而不是立即保存,那我很好。

    考虑到将来我们可能不再使用 SQL 服务器后端,我们可能会改用 oracle,甚至是 NOSQL 解决方案。当我切换后端时,我不希望我的所有 DA 层单元测试都中断。实际上是相反的,我需要我的 DA 层单元测试继续保持不变,以便我可以验证存储解决方案的切换不会影响 DA 层的语义或使用。

    【讨论】:

    • -1 即使您在抽象存储库和它们的实现之间有明确的分离,您可能仍然想测试实际的实现。我不同意这种分离,但认为因此测试 DAL 是不好的做法是一种逻辑谬误。
    • @Mark Seemann:我没有说测试 DAL 是不好的做法。我说过要测试 DAL 的用途(存储和检索内容),不要通过在 DAL 测试中包含特定于存储的代码来将测试与当前特定的 DAL 后端存储紧密耦合。
    【解决方案4】:

    好吧,让我们这样说吧:您不应该编写单元测试来确保实体框架本身工作。这是微软的工作。

    另外,有些人会争辩说,在“单元测试”中获取 SQL Server 数据库意味着您现在正在进行集成测试而不是单元测试。这个,不管你怎么做。不过,我不会争论这一点。

    您编写的单元测试应该是测试您自己的代码。

    您说您想直接读取数据库的原因之一是您的应用程序可能会看到缓存的数据。谁写了那个缓存?如果是你,那么一定要对它进行单元测试,尽管在这种情况下我可能会模拟数据库访问。

    但是,如果您关心的是 SQL Server(或实体框架)的缓存,那么您会从哪里开始呢?这些产品非常庞大,您不可能对整个系统进行单元测试。

    这并不意味着您应该假定 SQL Server 和实体框架没有缺陷。但您通常通过对应用程序进行集成测试而不是单元测试来捕获框架中的错误。

    【讨论】:

      【解决方案5】:

      正如 Craig 和其他人已经指出的那样:您正在进行集成测试。

      这意味着我必须用“否”来回答你的主要问题。

      但是,对于您的集成测试,您可以通过使用位于另一个应用程序域中的 EF 上下文来绕过任何内存缓存。每个 appdomain 都会看到自己的一组静态值,因此即使 EF 在版本 5 中使用静态缓存(我对此表示怀疑),您也可以以与 db 无关的方式对其进行测试。 (对您的软件支持的所有后端重复这些测试。)

      这是偏执的版本,顺便说一句。第二个上下文应该已经可以解决问题了。

      【讨论】:

        猜你喜欢
        • 2012-03-01
        • 1970-01-01
        • 2017-03-09
        • 1970-01-01
        • 2021-11-21
        • 2010-11-25
        • 2015-05-06
        • 2013-03-28
        • 1970-01-01
        相关资源
        最近更新 更多