使用EF4“代码优先”进行单元测试和存储库 [英] Unit testing with EF4 "Code First" and Repository

查看:210
本文介绍了使用EF4“代码优先”进行单元测试和存储库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图获得一个单元测试的处理一个非常简单的ASP.NET MVC测试应用程序,我在最新的EF4 CTP中使用Code First方法构建。我不是单位测试/嘲笑等经验。



这是我的Repository类:

  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();
返回条目;
}
}

这是IWeTrackerContext

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

...及其实现,WeightTrackerContext

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

在我的测试中,我有以下内容:

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

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

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

我的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 ,因为它没有构造函数。我得到的感觉,我正在咆哮错误的树,我的整个方法试图嘲笑我的上下文。任何建议将是最受欢迎的这个完整的单元测试新手。

解决方案

您通过Factory方法Set()创建一个DbSet上下文,但您不希望在单元测试中对EF进行任何依赖。因此,您需要查看的是使用IDbSet接口或使用Moq或RhinoMock之类的Mocking框架之一来实现DbSet的存根。假设你写了自己的Stub,你只需将WeightEntry对象添加到一个内部哈希集。



如果您搜索ObjectSet,您可能会更多运气学习单元测试EF IObjectSet。这些是DbSet在代码第一个CTP发布之前的对手,并从单元测试的角度对其进行了更多的描述。



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



作为对David的评论的回应,我将添加下面的附录,因为它不符合-comments。不确定这是否是长时间评论回复的最佳做法?



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



现在,您还将具有依赖于您将在实体中传递的IWeightTrackerContext的代码(即自定义存储库)框架WeightTrackerContext将实现IWeTrackerContext。这通常是通过使用诸如Unity的IoC框架的构造器注入来完成的。为了测试依赖于EF的存储库代码,您将通过MockContext实现,因此被测代码认为它正在与真实的EF和数据库进行交谈,并按预期行为(希望)。当您删除了可更改的外部数据库系统和EF的依赖性时,您可以在单元测试中可靠地验证存储库调用。



嘲笑框架的一大部分是提供验证Mock对象的调用以测试行为的能力。在上面的例子中,您的测试实际上只是测试DbSet添加功能,这不应该是您的关注,因为MS将对其进行单元测试。你想知道的是,在DbSet上添加的调用是在自己的存储库代码中进行的,如果适用的话,那就是Mock框架进入的位置。



对不起,我知道这是很多消化,但如果你阅读这篇文章很多将变得更加清晰,因为Scott Allen比我更好地解释这些东西:)


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.

This is my Repository class:

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;
    }
}

This is IWeightTrackerContext

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

...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);
}

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();
    }
}

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.

解决方案

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.

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.

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?

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.

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.

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.

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“代码优先”进行单元测试和存储库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆