实体框架4.1集成单元测试

时间:2011-08-17 15:17:59

标签: asp.net-mvc unit-testing asp.net-mvc-3 tdd ef-code-first

首先,一些警告:

我们正在建造一个中小型系统,预计寿命为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后从上下文中显式地分离每个对象以及在我们通过控制器中的上下文查询对象时进行分离来实现此功能,但我不想这样做只是为了让我们的单元测试工作,而且我相信这将是一个重大的性能影响。

问题:

有没有更好的方法与我们的控制器进行这种类型的集成测试?

1 个答案:

答案 0 :(得分:0)

也许您可以尝试创建opco的新实例,然后再将其传递给Edit(post)操作。要简化副本的创建,您可以使用AutoMapper,例如:

Mapper.CreateMap<OperatingCompany, OperatingCompany>();
opco = Mapper.Map<OperatingCompany, OperatingCompany>(opco);
相关问题