如何模拟在using()块中实例化的对象

时间:2013-03-08 16:46:29

标签: c# entity-framework unit-testing mocking using-statement

当读取/写入来自 Entity Framework 的上下文时,我经常读到,建议保持上下文的生命周期尽可能短(每个工作单元一个上下文)。这样做很有意义,但是我应该如何为每个方法中创建和处理此上下文的类编写单元测试?

让我们看一个简化的虚构示例代码段:

public class Resource : IResource {
    public Item GetItem(string name) {
        using(var context = new DbContext()) {
            return context.Items.FirstOrDefault(item => item.Name == name);
        }
    }
}

如果我想对资源进行单元测试,我想模拟DbContext,所以它会为我返回一些假数据。

我通常的方法是让DbContext成为我的类的属性并从外部注入它,如下所示:

public class Resource : IResource {
    public DbContext Context { private get; set; }

    public Item GetItem(string name) {
        return this.Context.Items.FirstOrDefault(item => item.Name == name);
    }
}

这样,注入上下文的instanciating类负责生命周期,我可以省略使用我当然可以注入一个模拟的上下文。

现在考虑到,提供长期生活环境可能是一个坏主意,并且遵循每个工作单元的一个上下文原则,这个选项是不利的。

那么我有什么选择?一个想法是注入一个ContextFactory,它按需创建和处理上下文:

public class Resource : IResource {
    public DbContextFactory ContextFactory { private get; set; }

    public Item GetItem(string name) {
        using(var context = ContextFactory.CreateContext()) {
            return context.Items.FirstOrDefault(item => item.Name == name);
        }
    }
}

这是否有意义或我是否会进入一个完全错误的指挥?

2 个答案:

答案 0 :(得分:1)

选项是使用方法注入:

   public Item GetItem(string name, DbContext context) {
        using(var context) {
            return context.Items.FirstOrDefault(item => item.Id == id);
        }
    }

Seemann在他的书Dependency Injection in .Net中也使用这种方法来注入依赖关系,这种依赖关系可以随着呼叫而变化。

但我会使用ContextFactory

答案 1 :(得分:1)

我检查了Mark Seemann的书(由Anton Sizikov提到),他实际上也使用了一家工厂,称之为短暂的一次性。它以某种方式是 Leaky Abstraction ,因为工厂管理一次性依赖的生命周期而不是DI容器。仍然比由于处理的共享依赖项导致的一次性使用或异常导致内存泄漏更好。