我正在尝试处理单元测试,这是一个非常简单的ASP.NET MVC测试应用程序,我在最新的EF4 CTP中使用代码优先的方法构建的。我对单元测试/模拟等不是很有经验。
这是我的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();
return entry;
}
}
这是IWeightTrackerContext
public interface IWeightTrackerContext
{
DbSet<WeightEntry> WeightEntries { get; set; }
int SaveChanges();
}
...and它的实现,WeightTrackerContext
public class WeightTrackerContext : DbContext, IWeightTrackerContext
{
public DbSet<WeightEntry> WeightEntries { get; set; }
}
在我的测试中,我有以下内容:
[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:
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<>
,因为它没有构造函数。我有一种感觉,我用我的整个方法试图模拟我的上下文,这是在找错地方。对于这个完整的单元测试新手来说,任何建议都是非常受欢迎的。
发布于 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的调用是在您自己的存储库代码中进行的,这就是模拟框架的用武之地。
对不起,我知道这篇文章很难理解,但如果你通读那篇文章,很多东西会变得更清楚,因为斯科特·艾伦在解释这些东西方面比我好得多:)
发布于 2011-02-17 05:45:45
在Daz所说的基础上(正如我在他的评论中提到的),您将需要创建一个IDbSet的“假”实现。CTP4的一个例子是here,但是为了让它工作,你必须定制你的Find方法,并为几个以前无效的方法添加返回值,比如Add。
以下是我为CTP 5制作的示例:
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; }
}
}
发布于 2013-10-12 14:56:35
您可能会收到一个错误
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集实现
public class MyDbSet<T> : IDbSet<T> where T : class
{
将此代码用作
public class MyContext : DbContext
{
public virtual MyDbSet<Price> Prices { get; set; }
}
如果你看下面的SO问题,2 2ns的答案,你应该能够找到一个自定义DbSet的完整实现。
Unit testing with EF4 "Code First" and Repository
然后,
var priceService = fixture.Create<PriceService>();
不应引发异常。
https://stackoverflow.com/questions/3463017
复制相似问题