首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用EF4“代码优先”和存储库进行单元测试

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

Stack Overflow用户
提问于 2010-08-12 05:32:01
回答 3查看 13.5K关注 0票数 49

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

这是我的Repository类:

代码语言:javascript
复制
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

代码语言:javascript
复制
public interface IWeightTrackerContext
{
    DbSet<WeightEntry> WeightEntries { get; set; }
    int SaveChanges();
}

...and它的实现,WeightTrackerContext

代码语言:javascript
复制
public class WeightTrackerContext : DbContext, IWeightTrackerContext
{
    public DbSet<WeightEntry> WeightEntries { get; set; }
}

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

代码语言:javascript
复制
[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:

代码语言:javascript
复制
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<>,因为它没有构造函数。我有一种感觉,我用我的整个方法试图模拟我的上下文,这是在找错地方。对于这个完整的单元测试新手来说,任何建议都是非常受欢迎的。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-08-12 17:16:52

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

如果你搜索ObjectSet和IObjectSet,你可能会更幸运地了解到单元测试EF。这些是DbSet在code first CTP发布之前的对等物,从单元测试的角度来看,还有更多关于它们的内容。

这里有一个关于MSDN的excellent article,讨论了EF代码的可测试性。它使用了IObjectSet,但我认为它仍然是相关的。

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

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

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

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

对不起,我知道这篇文章很难理解,但如果你通读那篇文章,很多东西会变得更清楚,因为斯科特·艾伦在解释这些东西方面比我好得多:)

票数 48
EN

Stack Overflow用户

发布于 2011-02-17 05:45:45

在Daz所说的基础上(正如我在他的评论中提到的),您将需要创建一个IDbSet的“假”实现。CTP4的一个例子是here,但是为了让它工作,你必须定制你的Find方法,并为几个以前无效的方法添加返回值,比如Add。

以下是我为CTP 5制作的示例:

代码语言:javascript
复制
public class InMemoryDbSet<T> : IDbSet<T> where T : class
{
    readonly HashSet<T> _data;
    readonly IQueryable _query;

    public InMemoryDbSet()
    {
        _data = new HashSet<T>();
        _query = _data.AsQueryable();
    }

    public T Add(T entity)
    {
        _data.Add(entity);
        return entity;
    }

    public T Attach(T entity)
    {
        _data.Add(entity);
        return entity;
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        throw new NotImplementedException();
    }

    public T Create()
    {
        return Activator.CreateInstance<T>();
    }

    public virtual T Find(params object[] keyValues)
    {
        throw new NotImplementedException("Derive from FakeDbSet and override Find");
    }

    public System.Collections.ObjectModel.ObservableCollection<T> Local
    {
        get { return new System.Collections.ObjectModel.ObservableCollection<T>(_data); }
    }

    public T Remove(T entity)
    {
        _data.Remove(entity);
        return entity;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _data.GetEnumerator();
    }

    public Type ElementType
    {
        get { return _query.ElementType; }
    }

    public Expression Expression
    {
        get { return _query.Expression; }
    }

    public IQueryProvider Provider
    {
        get { return _query.Provider; }
    }
}
票数 41
EN

Stack Overflow用户

发布于 2013-10-12 14:56:35

您可能会收到一个错误

代码语言:javascript
复制
 AutoFixture was unable to create an instance from System.Data.Entity.DbSet`1[...], most likely because it has no public constructor, is an abstract or non-public type.

DbSet有一个受保护的构造函数。

通常用于支持可测试性的方法创建您自己的Db集实现

代码语言:javascript
复制
public class MyDbSet<T> : IDbSet<T> where T : class
{

将此代码用作

代码语言:javascript
复制
public class MyContext : DbContext
{
    public virtual MyDbSet<Price> Prices { get; set; }
}

如果你看下面的SO问题,2 2ns的答案,你应该能够找到一个自定义DbSet的完整实现。

Unit testing with EF4 "Code First" and Repository

然后,

代码语言:javascript
复制
 var priceService = fixture.Create<PriceService>();

不应引发异常。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3463017

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档