如果您想要使用TDD(或任何其他高测试覆盖率的测试方法)和EF,您必须编写集成或端到端测试。这里的问题是,任何模拟上下文或存储库的方法都会创建测试,它可以测试您的上层逻辑(使用这些模拟)而不是您的应用程序。
简单的例子:
让我们定义通用的存储库:
public interface IGenericRepository<TEntity>
{
IQueryable<TEntity> GetQuery();
...
}
并让写一些商业方法:
public IEnumerable<MyEntity> DoSomethingImportant()
{
var data = MyEntityRepo.GetQuery().Select((e, i) => e);
...
}
现在,如果你嘲笑的库中,您将使用的LINQ到 - 对象和你将有一个绿色的测试,但如果你使用Linq-To-Entities运行应用程序,你将会得到一个异常,因为在L2E中不支持索引的选择过载。
这是一个简单的例子,但在查询和其他常见错误中使用方法会发生同样的情况。此外,这也会影响像Add,Update和Delete这样的方法,这些方法通常暴露在资源库上如果你不写一个模拟真正模拟EF上下文和参照完整性的行为,你将不会测试你的实现。
故事的另一部分是懒惰加载的问题,单元测试对于嘲笑也很难检测到。
因此,您还应该引入集成或端到端测试,这些测试将使用真实EF环境L2E与真实数据库一起工作。顺便说一句。需要使用端到端测试才能正确使用TDD。要在ASP.NET MVC中编写端到端测试,您可以使用WatiN,也可以使用SpecFlow来处理BDD,但这样做确实会增加很多工作量,但是您将会真正测试应用程序。如果您想了解更多关于TDD的信息,我建议您使用this book(唯一的缺点就是Java中的例子)。
集成测试如果不使用通用存储库,并且隐藏了某些不会暴露IQueryable
但直接返回数据的类中的查询,那么这种测试是有意义的。
例子:
public interface IMyEntityRepository
{
MyEntity GetById(int id);
MyEntity GetByName(string name);
}
现在你可以只写集成测试,测试执行这个仓库的,因为查询隐藏在这个类,而不是暴露到上层。但是这种类型的存储库在某种程度上被认为是与存储过程一起使用的旧实现。这种实现会失去很多ORM特性,或者你将不得不做很多额外的工作 - 例如,添加specification pattern以便能够在上层定义查询。
在ASP.NET MVC中,您可以用控制器级别的集成测试部分替换端到端测试。
编辑基于评论:
我不说,你需要的单元测试,集成测试和终端到终端的测试。我说做测试的应用程序需要更多的努力。所需测试的数量和类型取决于应用程序的复杂程度,应用程序的预期未来,其他团队成员的技能和技能。
小型直接项目可以在没有测试的情况下创建(好吧,这不是一个好主意,但我们都做到了,并且最终它可以工作),但是一旦项目通过某个阈值,您就会发现引入新功能或维护这个项目非常困难,因为你永远不知道它是否会破坏已经发挥作用的东西 - 这就是所谓的回归。防止回归的最佳防御措施是一套很好的自动化测试。
- 单元测试可以帮助您测试方法。理想情况下,这种测试应该覆盖方法中的所有执行路径。这些测试应该非常简短并且易于编写 - 复杂的部分可以是设置依赖关系(嘲讽,伪造,存根)。
- 集成测试可帮助您测试跨多个层的功能,并且通常跨多个进程(应用程序,数据库)进行测试。你不需要为他们提供所有的东西,而是更多地选择他们有用的地方。
- 端到端测试类似于用例/用户故事/功能验证。他们应该涵盖整个流程的要求。
不需要多次测试feture - 如果您知道该功能是在端到端测试中测试的,则无需为同一代码编写集成测试。此外,如果您知道该方法仅具有集成测试所涉及的单一执行路径,则无需为其编写单元测试。 TDD方法的效果要好得多,您可以从一个大测试(端到端或集成)开始,然后深入单元测试。
根据您的开发方法,您不必从头开始进行多种类型的测试,但随着应用程序变得越来越复杂,您可以稍后介绍它们。 TDD/BDD是个例外,你甚至在写单行其他代码之前,应该至少开始使用端到端和单元测试。
所以你问的是错误的问题。问题不是更简单?问题是最终会对你有什么帮助,什么复杂性适合你的应用程序?如果您想要轻松地进行单元测试的应用程序和业务逻辑,您应该将EF代码封装到可以被模拟的其他类中。但在同一时间,您必须引入其他类型的测试以确保EF代码正常工作。
我不能说你用什么办法将适合你的环境/项目/团队/等,但我可以从我过去的项目解释例如:
我工作的项目约5-6个月有两个的同事。该项目基于ASP.NET MVC 2 + jQuery + EFv4,并且是以增量和迭代的方式开发的。它有很多复杂的业务逻辑和大量复杂的数据库查询。我们从通用存储库和高代码覆盖率开始,使用单元测试+集成测试来验证映射(插入,删除,更新和选择实体的简单测试)。几个月后,我们发现我们的方法不起作用。我们有超过1.200个单元测试,代码覆盖率约为60%(这不是很好)以及很多回归问题。改变EF模型中的任何内容都可能在数周内未触及的部分引入意想不到的问题。我们发现我们缺少对我们的应用程序逻辑进行的集成测试或端对端测试。关于另一个项目的平行团队也得出了同样的结论,并且使用集成测试被认为是新项目的建议。
你说库增加了“复杂”到你的应用程序,但我要说他们是最初的'开销',使测试更容易。嘲笑一些存储库比嘲笑整个数据上下文更容易。 – Omar 2011-04-10 03:40:12
是的,但我不想在我目前的情况下开始的开销。我想快速申请进度。我已经失去了太多时间,没有任何实际进展。添加存储库会带来像IoC,DI等类似的东西。在我开始查看之前,我将不得不编写大量的测试。我知道这可能是正确的解决方案,但我不是在寻找“正确的”。我正在寻找简单的(虽然仍然可测试)的解决方案。 – Damb 2011-04-10 03:46:07