EF - 存储库工作单元

时间:2013-04-29 15:25:12

标签: entity-framework unit-testing repository-pattern

参考这篇文章IRepository - Entity implementation我还有一些疑问。 我的实体没有实现任何主键,也没有任何关于属性的东西来检测某些并发异常。

但是,这就是我要保持的行为。当我测试我的应用程序时,例如通过“内存reposiotry”,我永远不会得到任何“ConcurrencyException”,也不会重复键异常。此外,没有primay键实现,我无法执行任何Edit(T item)方法,因为我无法检索要编辑的实体。

我应该实现一些像“IEntityKey”,“IEntityConcurrency”这样的接口,以获得真正的解耦和可测试代码吗?

1 个答案:

答案 0 :(得分:0)

请注意,不要尝试测试存储库的内存实现。这没有价值,也不是测试的目的。您通常要测试的是使用存储库的代码,例如服务和业务逻辑代码,控制器操作等。

也就是说,您不必在所有测试中使用一个模拟存储库。对于不同的测试用例,拥有不同的测试存储库 - 甚至只是存储库的片段 - 通常是有意义的。

例如,如果您觉得需要在并发异常的情况下测试应用程序代码的行为,那么只需编写一个这样的模拟存储库方法:

public void Update(T entity)
{
    throw new DbConcurrencyException();
}

同样,当涉及多个线程,进程或用户作用于同一数据库时,您无法测试可能发生重复键异常的所有情况。要在出现此类异常的情况下测试应用程序的行为,请将其明确抛出。

如果你想测试你的应用程序中的一个Inserts链是否只使用唯一键那么是,像IEntityWithKey这样的接口可能会有所帮助(它也可以有一个复合键的两个属性)并使用像:

public void Add(T entity)
{
    if (InMemoryListOfT.Any(e => e.Key1 == entity.Key1 && e.Key2 == entity.Key2))
        throw new DuplicateKeyException();

    InMemoryListOfT.Add(entity);
}

如果在这种可能的情况下只涉及一个特定的实体,比如说Order,我甚至不会看到以通用的方式做这件事的必要性。对于这个特殊测试,您还可以使用这样的存储库方法,您不需要键接口:

public void Add(T entity)
{
    if (entity is Order)
    {
        var order = entity as Order;
        var inMemoryListOfOrder = InMemoryListOfT as List<Order>;

        if (inMemoryListOfOrder.Any(o => o.OrderId == order.OrderId))
            throw new DuplicateKeyException();
    }
    InMemoryListOfT.Add(entity);
}

从存储库返回单元测试数据通常属于&#34; Arrange&#34;测试的一部分。它说:如果我从存储库返回一个实体并调用这个或那个方法(&#34; Act&#34;)我希望得到以下结果(&#34; Assert&#34;)。检索实体的方式不是测试对象的一部分。您可以在测试存储库的new方法中创建一个Find的实体,如果您使用&#34;主键&#34;则无关紧要。为了这。如果要模拟失败的检索,只需从测试库返回null

无论你怎么努力,你都无法编写一个内存模拟实现,其行为与Entity Framework完全相同,数据库将在生产中运行。这是基础架构代码,您只能使用真正的EF提供程序和真实数据库系统进行集成测试。

您甚至无法信任有关任何LINQ查询的测试 - 无论是使用主键还是实体的任何其他属性 - 因为它们可能在内存中工作(LINQ到对象)但在出现异常时失败使用实体框架(LINQ到实体),如果它们失败或者甚至可能依赖于EF提供者和您的数据库系统。

以下是关于您在尝试测试实体框架查询时遇到的问题的a great detailed answer and lots of examples