Wal*_*ter 5 unit-testing entity-framework in-memory-database
这比实际问题更能解决/解决问题.我在这里发帖,因为我在堆栈溢出时找不到这个解决方案,或者确实经过很多谷歌搜索.
问题:
我首先使用EF 4代码创建一个MVC 3 webapp,我想编写单元测试.我也在使用NCrunch来代码运行单元测试,所以我想避免在这里支持实际的数据库.
其他方案:
IDataContext
我发现这是创建内存datacontext最常用的方法.它实际上涉及为MyDataContext编写接口IMyDataContext,然后在所有控制器中使用该接口.这样做的一个例子是在这里.
这是我最初使用的路线,我甚至编写了一个T4模板,从MyDataContext中提取IMyDataContext,因为我不喜欢维护重复的相关代码.
但是我很快发现在使用IMyDataContext而不是MyDataContext时,一些Linq语句在生产中失败了.特别是像这样的查询会抛出NotSupportedException
var siteList = from iSite in MyDataContext.Sites
let iMaxPageImpression = (from iPage in MyDataContext.Pages where iSite.SiteId == iPage.SiteId select iPage.AvgMonthlyImpressions).Max()
select new { Site = iSite, MaxImpressions = iMaxPageImpression };
Run Code Online (Sandbox Code Playgroud)
我的解决方案
这其实很简单.我只是创建一个MyInMemoryDataContext子类到MyDataContext并覆盖所有IDbSet <..>属性,如下所示:
public class InMemoryDataContext : MyDataContext, IObjectContextAdapter
{
/// <summary>Whether SaveChanges() was called on the DataContext</summary>
public bool SaveChangesWasCalled { get; private set; }
public InMemoryDataContext()
{
InitializeDataContextProperties();
SaveChangesWasCalled = false;
}
/// <summary>
/// Initialize all MyDataContext properties with appropriate container types
/// </summary>
private void InitializeDataContextProperties()
{
Type myType = GetType().BaseType; // We have to do this since private Property.Set methods are not accessible through GetType()
// ** Initialize all IDbSet<T> properties with CollectionDbSet<T> instances
var DbSets = myType.GetProperties().Where(x => x.PropertyType.IsGenericType && x.PropertyType.GetGenericTypeDefinition() == typeof(IDbSet<>)).ToList();
foreach (var iDbSetProperty in DbSets)
{
var concreteCollectionType = typeof(CollectionDbSet<>).MakeGenericType(iDbSetProperty.PropertyType.GetGenericArguments());
var collectionInstance = Activator.CreateInstance(concreteCollectionType);
iDbSetProperty.SetValue(this, collectionInstance,null);
}
}
ObjectContext IObjectContextAdapter.ObjectContext
{
get { return null; }
}
public override int SaveChanges()
{
SaveChangesWasCalled = true;
return -1;
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下我CollectionDbSet <>是FakeDbSet <>的一个略加修改这里(其简单地与下面的ObservableCollection和ObservableCollection.AsQueryable实现IDbSet()).
这个解决方案适用于我的所有单元测试,特别是NCrunch即时运行这些测试.
完整的集成测试
这些单元测试测试所有业务逻辑,但一个主要缺点是您的LINQ语句都不能保证与您的实际MyDataContext一起使用.这是因为针对内存数据上下文进行测试意味着您正在替换Linq-To-Entity提供程序,而不是Linq-To-Objects提供程序(正如在这个 SO问题的答案中指出的那样).
为了解决这个问题,我在单元测试中使用Ninject,并在单元测试中设置InMemoryDataContext而不是MyDataContext.然后,您可以在运行集成测试时使用Ninject绑定到实际的MyDataContext(通过app.config中的设置).
if(Global.RunIntegrationTest)
DependencyInjector.Bind<MyDataContext>().To<MyDataContext>().InSingletonScope();
else
DependencyInjector.Bind<MyDataContext>().To<InMemoryDataContext>().InSingletonScope();
Run Code Online (Sandbox Code Playgroud)
如果您对此有任何反馈,请告诉我,但总会有改进.
| 归档时间: |
|
| 查看次数: |
1189 次 |
| 最近记录: |