【问题标题】:Building a testable MVC3 & EF 4.1 app [closed]构建可测试的 MVC3 和 EF 4.1 应用程序 [关闭]
【发布时间】:2011-11-09 17:14:42
【问题描述】:

首先,对于这个问题的开放性,我深表歉意。然而,我已经为此瘫痪了好几个月,尽管征求了同意,但仍然无法克服这一点。

我一直在开发 MVC/EF 应用程序。我试图了解如何设计和构建由实体框架(4.1)支持的可测试 MVC3 应用程序。您可以看到我就主题hereherehere 提出的一些问题。

我尽量不让它过于复杂,但我希望它是一个健全的、松散耦合的设计,可以增长。根据我的理解,以下几乎是最低要求的组件:

MVC 应用
这是非常薄。尽可能少的逻辑在这里。我的视图具有尽可能少的条件逻辑,我的视图模型永远不会超过 POCO,我的控制器只处理视图模型和域模型之间的映射,并调用服务。

服务层 + 接口(单独的程序集)
这就是我所有的业务逻辑所在。这样做的目的是能够在此之上添加任何瘦客户端(表单应用程序、移动应用程序、Web 服务)以暴露我的应用程序的核心。服务层的接口位于另一个程序集中。

核心实用程序/横切 + 接口(单独的程序集)
这是我构建的不是特定于我的应用程序的东西,但不是我正在使用的框架或任何第三方插件的一部分。同样,这些组件的接口位于它们自己的程序集中。

存储库(EF 上下文)
这是我的域模型和我的数据库之间的接口。我的服务层使用它通过域模型检索/修改我的数据库。

域模型(EF POCO)
EF4 生成 POCO。其中一些可能会为了方便而扩展到其他嵌套属性或计算属性(例如Order.Total = Order.Details.Sum(d => d.Price)

IoC 容器
这是用于将我的具体/虚假依赖项(服务/实用程序)注入 MVC 应用程序和服务的内容。构造函数注入贯穿始终。

这是我苦苦挣扎的地方:

1) 当集成测试与单元测试比较合适时。例如,某些程序集是否需要混合使用两者,还是主要针对 MVC 应用程序进行集成测试,而针对我的服务和实用程序进行单元测试?

2) 我是否会费心为存储库/域模型代码编写测试?当然,对于 POCO,这是不适用的。但是当我使用计算属性扩展我的 POCO 时呢?

3) 用于存储库的正确模式。我知道这是非常主观的,因为每次我看到这个讨论,似乎每个人都有不同的方法。因此,很难弄清楚该走哪条路。例如,我是滚动自己的存储库,还是直接使用 EF (DbContext)?

4) 当我为我的服务编写测试时,我是模拟我的存储库,还是使用 SQL Lite 构建一个模拟数据库并针对它进行测试? (参见辩论 herehere)。

5) 这是全有或全无的事情,例如,如果我进行任何测试,我应该测试所有内容吗?或者,是否有任何测试总比没有测试好?如果是后者,首先要打的更重要的领域是哪里(我在考虑服务层)?

6) 是否有任何好书、文章或示例应用程序可以帮助我回答大部分这些问题?

我认为现在就足够了。如果这最终过于开放,请告诉我,我会很乐意关闭。但同样,我已经花了几个月的时间试图靠自己解决这个问题。

【问题讨论】:

    标签: asp.net-mvc unit-testing entity-framework architecture


    【解决方案1】:

    这是一个非常复杂的问题。你的每一个观点都足够大,可以作为一个单独的问题,所以我只写简短的总结:

    1. 集成测试和单元测试不会相互替代。如果您想拥有经过良好测试的应用程序,您总是需要两者。单元测试用于单独测试逻辑(通常在模拟、存根、假货等的帮助下),而集成测试用于测试您的组件是否一起正常工作(= 没有模拟、存根或假货)。何时使用集成测试以及何时使用单元测试实际上取决于您正在测试的代码和您遵循的开发方法(例如TDD)。

    2. 如果您的 POCO 包含任何逻辑,您应该为它们编写单元测试。存储库中的逻辑通常严重依赖于数据库,因此在没有数据库的情况下模拟上下文和测试它们通常是无用的,因此您应该使用集成测试来覆盖它们。

    3. 这实际上取决于您对存储库的期望。如果它只是愚蠢的 DbContext / DbSet 包装器,那么存储库的值为零,并且它很可能不会使您的代码单元可测试,如某些参考辩论中所述。如果它包装查询(上层没有 LINQ-to-entites),公开对聚合根的访问,那么存储库的含义是正确分离数据访问并公开可模拟接口。

    4. 它完全依赖于前一点。如果您公开IQueryable 或接受Expression<Func<>> 的方法在内部传递给IQueryable you cannot correctly mock the repository(当然可以,但您仍然需要将每个单元测试与集成测试测试相同的逻辑配对)。 LINQ-to-entities 是“副作用”/泄漏抽象。如果您将查询完全包装在存储库中并使用您自己的声明性查询语言(规范模式),您可以模拟它们。

    5. 任何测试都比没有测试好。许多方法期望高密度覆盖。 TDD 甚至达到 100% 的测试覆盖率,因为测试总是首先编写,没有测试就没有逻辑。如果您需要对一段代码进行测试,这与您所遵循的方法以及您的专业决定有关。

    6. 我不认为有任何“阅读此内容,您就会知道如何去做”。这是软件工程,软件工程是一门艺术。没有适用于所有情况的蓝图(在大多数情况下都不适用)。

    【讨论】:

    • 谢谢,拉迪斯拉夫。我知道这不是一个容易回答的问题,无论如何,我很感谢你尝试一下。澄清一下,我可能会有愚蠢的存储库,并将 LINQ-to-entities 逻辑保留在我的服务层中。所以它真的会在我的服务层中承担很多责任(业务+查询逻辑)。这是否会导致其自身的问题——即,与拥有我的 LINQ 到实体逻辑的存储库并将我的服务层保持严格的业务逻辑相比,有什么权衡?再说一次,我知道这是主观的,我只是想确保我理解权衡。
    • 作为题外话,我想感谢你对 SO 社区的贡献,特别是对我的问题的贡献,因为你似乎经常回答我的很多问题,其中大部分似乎需要您花费大量时间和思考。它不会不受欢迎。
    猜你喜欢
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多