【发布时间】:2011-08-17 15:17:58
【问题描述】:
首先,有几点注意事项:
我们正在构建一个预计使用寿命为 2-5 年(相对较短)的中小型系统。该系统正在使用 Entity Framework 4.1、Code First、MVC3 进行开发。我们正在努力推动我们的团队进行单元测试/TDD。这是我们尚未做过的事情,但我们认识到它的价值,因此正在朝着这个方向迈出一小步。
考虑到所有这些,我们决定使用 Visual Studio 2010 的内置测试框架构建单元测试。我们没有使用存储库模式或模拟。这是为了减少构建单元测试的复杂性和时间。每个单元测试实际上更像是一个集成测试——我们有一个“测试”数据库,每次运行测试时都会初始化,每个测试都使用视图使用相同的控制器来尽可能接近真实世界系统的行为。
在评论或回答这个问题时,请不要攻击这种方法。我很清楚这不是最纯粹的 TDD,也不是测试工作单元的理想模式。我们这样做是为了让我们对这项技术有所了解,并尝试在构建单元测试所花费的时间方面获得最大的收益。
问题:
我们正在构建大量单元测试,它们通过控制器存储对象,然后通过控制器从数据库中检索对象,以验证它是否正确存储,并且它是否以我们期望的方式通过我们的控制器返回。这是一个例子:
[TestMethod]
public void Edit_Post_Saves_OperatingCompany_In_DB_When_OperatingCompany_Is_Valid()
{
OperatingCompany opco = new OperatingCompany();
opco.Name = "OpCo - Edit Post - Valid - DB Save";
controller = new OperatingCompanyController();
controller.Create(opco);
Guid opcoid = opco.Id;
controller = new OperatingCompanyController();
opco = (OperatingCompany) ((ViewResult)controller.Edit(opcoid)).Model;
opco.Name = "Edit - OpCo - Edit Post - Valid - DB Save";
controller = new OperatingCompanyController();
HelperMethods.AddValidationResultsToModelState(opco, controller);
controller.Edit(opco);
controller = new OperatingCompanyController();
ViewResult result = controller.Index();
Assert.IsTrue(((IEnumerable<OperatingCompany>)result.Model).Contains(opco));
}
我们得到的错误是这样的:
测试方法 WebObjects.Tests.Controllers.OperatingCompanyControllerTest.Edit_Post_Saves_OperatingCompany_In_DB_When_OperatingCompany_Is_Valid 抛出异常: System.InvalidOperationException:一个实体对象不能被多个 IEntityChangeTracker 实例引用。
我很确定会发生这种情况,因为有问题的实体没有从第一个控制器的上下文中分离出来,当我们重新实例化它并尝试编辑同一个实体时,它认为另一个上下文仍然持有它.我们重新实例化控制器的原因是确保实体从数据库中被拉回,而不仅仅是从 DbContext 的缓存中返回(如果我在这里错了,请纠正我,但我相信这会发生如果我们没有重新实例化)。
我们能够通过在每次调用 SaveChanges 后显式地从上下文中分离每个对象以及在我们通过控制器中的上下文查询对象时分离来实现这一点,但我不想这样做只是为了让我们的单元测试正常工作,而且我相信这会对性能产生重大影响。
问题:
有没有更好的方法来使用我们的控制器执行这种类型的集成测试?
【问题讨论】:
标签: asp.net-mvc unit-testing asp.net-mvc-3 tdd ef-code-first