当存储库保存最重要的代码时,编写单元测试

时间:2014-04-08 09:53:43

标签: c# unit-testing ninject repository-pattern entity-attribute-value

我有一个EAV系统,它将实体存储在SQL数据库中,将它们取出并存储在缓存中。应用程序是使用存储库模式编写的,因为在将来的某个时候,我们可能会切换到使用NOSQL数据库来提供部分或全部数据。我使用Ninject在运行时获取正确的存储库。

系统的很大一部分功能是以高效和及时的方式存储,检索和查询数据。没有大量的功能不属于数据访问或用户界面领域。

我已经阅读了单元测试 - 我理解了这个理论,但由于一些原因尚未付诸实践:

  • 实体由字段集,字段,值组成,每个字段都有许多属性。在代码中创建任何大量这些以进行测试需要付出很多努力。
  • 我的代码中一些最重要的部分位于存储库中。例如,所有数据访问都通过一个高度优化的方法,从数据库或缓存中获取实体。
  • 使用测试数据库感觉就像我打破了单元测试的关键原则之一 - 没有外部依赖。

除此之外,构建存储库的方式感觉与数据在SQL中的存储方式有关。实体放在一个表中,字段放在另一个表中,值放在另一个表中。所以我有一个存储库。我的理解是,在文档存储数据库中,实体,其字段和值都将作为单个对象存在,从而无需多个存储库。我已经考虑过让数据访问更加精细,以便将代码段移到存储库之外,但这会通过强制我以专门用于从SQL检索数据的方式编写存储库接口来解决问题。 p>

问题:基于以上所述,我是否应该接受我不能为代码的大部分内容编写单元测试而只是测试我能做的事情?

3 个答案:

答案 0 :(得分:3)

  

我应该接受我不能为我的大部分代码编写单元测试吗?

不,你不应该接受。事实上,情况永远不会如此 - 只要付出足够的努力,你就可以对任何事情进行单元测试。

您的问题归结为:您的代码依赖于数据库,但您无法使用它,因为它是外部依赖项。您可以使用mock objects - 在单元测试代码中构建的特殊对象来解决此问题,这些对象将自身呈现为数据库接口的实现,并为您的程序提供完成特定单元测试所需的数据。当您的程序向这些对象发送请求时,您的单元测试代码可以验证请求是否正确。当您的程序需要特定响应时,您的单元测试会根据单元测试场景的要求为其提供响应。

模拟可能并非易事,特别是在请求和响应很复杂的情况下。有几个库可以帮助你在.NET中使用它,使得模拟对象的编码任务几乎与真实对象的结构无关。然而,真正的复杂性往往是你正在嘲笑的系统的行为 - 在你的情况下,那就是数据库。编写这种复杂性的工作完全取决于您,它确实消耗了相当大一部分编码时间。

答案 1 :(得分:2)

看来,当你说“单元测试”时,你的意思是“集成测试”。因为在单元测试世界中没有数据库。如果您希望获取或插入一些数据到外部资源,您只需伪造它(使用模拟,存根,假货,间谍等)

  

我应该接受我不能为我的大部分内容编写单元测试   代码,只是测试我能做的事情?

很难说没有看到你的代码,但听起来你可以很容易地进行单元测试。这是基于您对接口和存储库模式的使用。只要单元测试独立于其他测试,测试只有一个功能,小巧,简单,不依赖于任何外部资源 - 你很高兴。

不要将此与集成和其他类型的测试混淆。这些可能涉及实际数据,写起来可能有点棘手。

答案 2 :(得分:0)

如果您正在使用正确的存储库模式测试很容易,因为

  1. 业务层仅了解存储库接口,该接口仅处理该层已知的对象,并且不会暴露与实际Db相关的任何内容(如EF)。您可以在这里使用实现存储库界面的假货。

  2. 测试数据库访问意味着您正在测试Repo实现,您可以获取和输出测试对象。将存储库与数据库结合起来是很自然的。

  3. 对repo的测试应该是这样的

    public void add_get_object()
     {
         var entity=CreateSomeTestEntity();
         _repo.Save(entity);
         var loaded=_repo.Get(entity.Id);
         //assert those 2 entities are identical, but NOT using reference
         // either make it implement IEquatable<Entity> or test each property manually
      }
    

    这些repo测试可以在任何存储库实现中重复使用:在内存,EF,raven db等中,因为它不应该与实现有关,重要的是repo做了它需要做的事情(保存/加载业务对象)。