使用 EF4“代码优先"进行单元测试和存储库

Unit testing with EF4 quot;Code Firstquot; and Repository(使用 EF4“代码优先进行单元测试和存储库)
本文介绍了使用 EF4“代码优先"进行单元测试和存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我正在尝试对一个非常简单的 ASP.NET MVC 测试应用程序进行单元测试,这是我在最新的 EF4 CTP 中使用 Code First 方法构建的.我对单元测试/模拟等不是很有经验.

I am attempting to get a handle on Unit testing a very simple ASP.NET MVC test app I've built using the Code First approach in the latest EF4 CTP. I'm not very experience with Unit testing / mocking etc.

这是我的存储库类:

public class WeightTrackerRepository
{
    public WeightTrackerRepository()
    {
        _context = new WeightTrackerContext();
    }

    public WeightTrackerRepository(IWeightTrackerContext context)
    {
        _context = context;
    }

    IWeightTrackerContext _context;

    public List<WeightEntry> GetAllWeightEntries()
    {
        return _context.WeightEntries.ToList();
    }

    public WeightEntry AddWeightEntry(WeightEntry entry)
    {
        _context.WeightEntries.Add(entry);
        _context.SaveChanges();
        return entry;
    }
}

这是 IWeightTrackerContext

This is IWeightTrackerContext

public interface IWeightTrackerContext
{
    DbSet<WeightEntry> WeightEntries { get; set; }
    int SaveChanges();
}

...及其实现,WeightTrackerContext

...and its implementation, WeightTrackerContext

public class WeightTrackerContext : DbContext, IWeightTrackerContext
{
    public DbSet<WeightEntry> WeightEntries { get; set; }
}

在我的测试中,我有以下几点:

In my test, I have the following:

[TestMethod]
public void Get_All_Weight_Entries_Returns_All_Weight_Entries()
{
    // Arrange
    WeightTrackerRepository repos = new WeightTrackerRepository(new MockWeightTrackerContext());

    // Act
    List<WeightEntry> entries = repos.GetAllWeightEntries();

    // Assert
    Assert.AreEqual(5, entries.Count);
}

还有我的 MockWeightTrackerContext:

And my MockWeightTrackerContext:

class MockWeightTrackerContext : IWeightTrackerContext
{
    public MockWeightTrackerContext()
    {
        WeightEntries = new DbSet<WeightEntry>();
        WeightEntries.Add(new WeightEntry() { Date = DateTime.Parse("01/06/2010"), Id = 1, WeightInGrams = 11200 });
        WeightEntries.Add(new WeightEntry() { Date = DateTime.Parse("08/06/2010"), Id = 2, WeightInGrams = 11150 });
        WeightEntries.Add(new WeightEntry() { Date = DateTime.Parse("15/06/2010"), Id = 3, WeightInGrams = 11120 });
        WeightEntries.Add(new WeightEntry() { Date = DateTime.Parse("22/06/2010"), Id = 4, WeightInGrams = 11100 });
        WeightEntries.Add(new WeightEntry() { Date = DateTime.Parse("29/06/2010"), Id = 5, WeightInGrams = 11080 });
    }

    public DbSet<WeightEntry> WeightEntries { get;set; }

    public int SaveChanges()
    {
        throw new NotImplementedException();
    }
}

当我尝试构建一些测试数据时会出现我的问题,因为我无法创建 DbSet<> 因为它没有构造函数.我觉得我用我的整个方法试图模仿我的上下文是在叫错树.任何建议都欢迎这个完整的单元测试新手.

My problem occurs when I'm trying to build up some test data as I can't create a DbSet<> as it has no constructor. I get the feeling I'm barking up the wrong tree with my whole approach trying to mock my context. Any advice would be most welcome to this complete unit testing newbie.

推荐答案

您通过 Context 上的工厂方法 Set() 创建了一个 DbSet,但您不希望在单元测试中对 EF 有任何依赖.因此,您需要考虑的是使用 IDbSet 接口实现 DbSet 存根,或者使用 Moq 或 RhinoMock 等 Mocking 框架之一实现存根.假设您编写了自己的 Stub,您只需将 WeightEntry 对象添加到内部哈希集.

You create a DbSet through the Factory method Set() on the Context but you don't want any dependency on EF in your unit test. Therefore, what you need to look at doing is implementing a stub of DbSet using the IDbSet interface or a Stub using one of the Mocking frameworks such as Moq or RhinoMock. Assuming you wrote your own Stub you'd just add the WeightEntry objects to an internal hashset.

如果您搜索 ObjectSet 和 IObjectSet,您可能会更幸运地了解单元测试 EF.这些是代码优先 CTP 发布之前的 DbSet 的对应物,并且从单元测试的角度写了很多关于它们的文章.

You may have more luck learning about unit testing EF if you search for ObjectSet and IObjectSet. These are the counterparts to DbSet prior to the code first CTP release and have a lot more written about them from a unit testing perspective.

这是 MSDN 上讨论 EF 代码可测试性的优秀文章.它使用 IObjectSet 但我认为它仍然相关.

Here's an excellent article on MSDN that discusses testability of EF code. It uses IObjectSet but I think it's still relevant.

作为对大卫评论的回应,我在下面添加了这个附录,因为它不适合 - 评论.不确定这是否是长评论回复的最佳做法?

As a response to David's comment I'm adding this addendum below as it wouldn't fit in the -comments. Not sure if this is the best practice for long comment responses?

您应该更改 IWeightTrackerContext 接口以从 WeightEntries 属性返回 IDbSet 而不是 DbSet 具体类型.然后,您可以使用模拟框架(推荐)或您自己的自定义存根创建 MockContext.这将从 WeightEntries 属性返回一个 StubDbSet.

You should change the IWeightTrackerContext interface to return an IDbSet from the WeightEntries property rather than a DbSet concrete type. You can then create a MockContext either with a mocking framework (recommended) or your own custom stub. This would return a StubDbSet from the WeightEntries property.

现在,您还将拥有依赖于 IWeightTrackerContext 的代码(即自定义存储库),在生产中您将传递将实现 IWeightTrackerContext 的实体框架 WeightTrackerContext.这往往是通过使用 IoC 框架(如 Unity)的构造函数注入来完成的.为了测试依赖于 EF 的存储库代码,您将传入您的 MockContext 实现,以便被测代码认为它正在与真实"的 EF 和数据库通信,并按预期运行(希望如此).由于您已经消除了对可变外部数据库系统和 EF 的依赖,因此您可以在单元测试中可靠地验证存储库调用.

Now you will also have code (i.e Custom Repositories) that depend on the IWeightTrackerContext which in production you would pass in your Entity Framework WeightTrackerContext that would implement IWeightTrackerContext. This tends to be done through constructor injection using an IoC framework such as Unity. For testing the repository code that depends on EF you would pass in your MockContext implementation so the code under test thinks it's talking to the "real" EF and database and behaves (hopefully) as expected. As you have removed the dependancy on the changeable external db system and EF you can then reliably verify repository calls in your unit tests.

模拟框架的很大一部分是提供验证对 Mock 对象的调用以测试行为的能力.在上面的示例中,您的测试实际上只是测试 DbSet Add 功能,这不应该是您关心的问题,因为 MS 将对此进行单元测试.您想知道的是,如果合适的话,对 Add on DbSet 的调用是在您自己的存储库代码中进行的,而这正是 Mock 框架的用武之地.

A big part of the mocking frameworks is providing the ability to verify calls on Mock objects to test behaviour. In your example above your test is actually only testing the DbSet Add functionality which shouldn't be your concern as MS will have unit tests for that. What you'd want to know is that the call to the Add on DbSet was made from within your own repository code if appropriate and that is where the Mock frameworks come in.

抱歉,我知道这需要消化很多东西,但是如果你阅读了那篇文章,你会变得更清楚,因为 Scott Allen 比我更擅长解释这些东西 :)

Sorry I know this is a lot to digest but if you have a read through that article a lot will become clearer as Scott Allen is a lot better at explaining this stuff than I am :)

这篇关于使用 EF4“代码优先"进行单元测试和存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

相关文档推荐

How to MOQ an Indexed property(如何最小起订量索引属性)
Mocking generic methods in Moq without specifying T(在 Moq 中模拟泛型方法而不指定 T)
How Moles Isolation framework is implemented?(Moles Isolation 框架是如何实现的?)
Difference between Dependency Injection and Mocking Framework (Ninject vs RhinoMocks or Moq)(依赖注入和模拟框架之间的区别(Ninject vs RhinoMocks 或 Moq))
How to mock Controller.User using moq(如何使用 moq 模拟 Controller.User)
How do I mock a class without an interface?(如何模拟没有接口的类?)