应如何将 EF 代码优先 DAL 的集成测试和 DAL 客户端的纯单元测试分开



在看到一些反对针对模拟测试 EF 的强烈建议之后,尤其是代码优先,我决定针对专用于测试的 SqlCe 数据库进行集成测试,然后在 DbContext 和 DbSet 提供的工作单元和存储库的下游使用纯单元测试。

我只是不清楚在哪里画线以及在哪里测试什么。我知道当我确信 DAL 特定的集成测试覆盖其内部时,我可以在我的服务层中模拟 DAL,但是我在 DAL 中测试什么?测试似乎没有太多意义来查看我是否可以保存和读取对象,因为 EF 是外部的并且已经过测试。

你将使用集成测试在 DAL 中测试映射和查询。例:

public class Service {
   private readonly IDAL _dal;
   public Service(IDAL dal) {
       // Not null validation here
       _dal = dal;
   }
   public void DoSomething() {
       SomeData data = FindSomeData();
       // Do some logic
       _dal.Commit();
   }
   protected virtual SomeData FindSomeData() {
       return _dal.SomeData.Where(...).FirstOrDefault();
   }
}

这是一个非常简单的示例,显示:

  • Service取决于DALDAL接口通过构造函数注入传递。
  • Service包含要测试的公共 DoSomething 方法,以了解逻辑是否正确执行。但这种方法也依赖于数据库查询和数据库持久性(Commit(。
  • 查询是
  • 逻辑的一部分,但执行此类查询是单独的问题,因此它由自己的方法处理。在更复杂的情况下,此方法可以在其他类中注入到Service类(存储库(中。这些查询方法的关键条件是:
    • 他们不回来IQueryable
    • 他们不接受Expression<>作为参数

如何单元测试DoSomething方法:

  • 在这个简单的示例中,测试类将从类派生Service并重写FindSomeData以返回测试数据。在注入的情况下,您将改为为注入类定义 false。
  • 您还将模拟IDAL,并且可以验证是否调用了Commit

您应该使用什么集成测试:

  • 您应该为查询真实数据库FindSomeData创建测试
  • 一般来说,您还应该对Commit进行集成测试,但这更难实现,因为该示例直接从DoSomething调用了提交。您不想再次测试该方法,同时该方法Commit泛型情况太多,因为它只是将所有更改从当前上下文刷新到数据库。我通常有单独的测试来插入、更新和删除每个实体类型。当DoSomething方法进行一些复杂的修改时,您可以将该方法拆分为两个方法,一个由实际逻辑的单元测试处理,另一个由逻辑生成的不同持久性方案的集成测试涵盖。

实体框架已测试,但您的 DAL(尤其是映射(未经过测试。我更喜欢进行集成测试,以向我表明至少我的映射是正确的,更好的是,我可以成功地对我的数据库执行所有 CRUD 操作。

我们通常测试数据库方面的是,复杂的对象图是否可以正确插入、更新、删除;基本上测试更复杂的映射。
在我看来,测试是否可以插入具有 3 个原始值属性的对象等等并没有多大意义,因为那样你永远不会看到它的结束。
我们有点喜欢乐观(简单的东西会起作用(,我们测试更复杂的关联,如果我们在映射中遇到错误(例如,应该删除但没有删除的对象(,我们为此编写一个额外的测试。
从业务角度绝对测试所有内容通常是不明智的;你应该先关注高风险/高伤害的东西,然后沿着严重性阶梯向下努力,直到你认为它不再值得。

1(有一组集成测试来测试你的映射

2( 使 DAL 非常轻量级,但具有足够的能力来构造查询,如下所示:

public interface IDb
{
    IQueryable<T>Query<T>();
    ... (save, delete, get-by-id methods)...
}

3( 编写对象,这些对象封装了针对 DAL 构造查询背后的逻辑

public class MuppetSearch
{
    public MuppetColor? Color { get; set;}
    public string Name{ get; set; }
    public IQueryable<Muppet> ConstructQuery(IDb db)
    {
        var query = db.Query<Muppet>();
        if(Color.HasValue)
        {
            query = query.Where(m=>m.Value == Color.Value);
        }
        if(!String.IsNullOrEmpty(Name))
        {
            query = query.Where(m=>m.Name.Contains(Name));
        }
        return query;
    }
}

4(测试这些,模拟你需要的所有数据应该是微不足道的

5( 在您的服务中使用搜索类进行查询构造

最新更新