测试实体框架查找方法
Posted
技术标签:
【中文标题】测试实体框架查找方法【英文标题】:Test Entity Framework Find Method 【发布时间】:2013-11-08 00:17:15 【问题描述】:我正在尝试测试 SystemService.cs
中的 GetSystem(int id)
方法是否返回正确的值,但似乎无法弄清楚如何让所有内容一起发挥作用。似乎无论我做什么,GetSystem()
总是返回 null。这是使用实体框架 6。如果我将 GetSystem 的主体更改为 _context.Systems.SingleOrDefault(s => s.Id = id)
,那么一切正常,但我真的很想使用 Find()
。
测试这个的正确方法是什么?我在这个例子中使用 xUnit 和 Moq。 SystemServiceTests.cs
显示我当前使用的代码不起作用。
SystemService.cs
namespace MyProject.Services
public class SystemService
private readonly MyContext _context;
public SystemService(MyContext context)
_context = context;
public Models.System GetSystem(int id)
return _context.Systems.Find(id);
SystemServiceTests.cs
namespace MyProject.Tests.Unit
public class SystemServiceTests
[Fact]
public void GetSystemReturnsFromContext()
var data = new List<Models.System>
new Models.System Id = 1, Name = "test 1" ,
new Models.System Id = 2, Name = "test 2"
.AsQueryable();
var mockContext = new Mock<MyContext>();
var mockSet = new Mock<MockableDbSetWithIQueryable<Models.System>>();
mockContext.Setup(c => c.Systems).Returns(mockSet.Object);
mockSet.Setup(m => m.Provider).Returns(data.Provider);
mockSet.Setup(m => m.Expression).Returns(data.Expression);
mockSet.Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var service = new SystemService(mockContext.Object);
var system = service.GetSystem(1);
Assert.NotNull(system); // This is always null
MyContext.cs
namespace MyProject.Models
public class MyContext : DbContext
public MyContext()
: base("DefaultConnection")
public virtual DbSet<Models.System> Systems get; set;
System.cs
namespace MyProject.Models
public class System
public int Id get; set;
public string Name get; set;
MockableDbSetWithIQueryable.cs
namespace MyProject.Tests.Helpers
public abstract class MockableDbSetWithIQueryable<T> : DbSet<T>, IQueryable
where T : class
public abstract IEnumerator<T> GetEnumerator();
public abstract Expression Expression get;
public abstract Type ElementType get;
public abstract IQueryProvider Provider get;
PS。一些代码,特别是MockableDbSetWithIQueryable
,可以在http://msdn.microsoft.com/en-US/data/dn314429找到
【问题讨论】:
不能mockContext.Setup(c => c.Systems).Returns(data);
吗?
【参考方案1】:
我能够找到推荐的方法来使用 Entity Framework 6 测试所有内容。此推荐的资源可在 http://msdn.microsoft.com/en-US/data/dn314431 获得。
简而言之,需要为需要测试的每个位创建测试类。我最终做的是以下内容:
TestDbSet.cs
public class TestDbSet<TEntity> : DbSet<TEntity>, IQueryable, IEnumerable<TEntity>
where TEntity : class
ObservableCollection<TEntity> _data;
IQueryable _query;
public TestDbSet()
_data = new ObservableCollection<TEntity>();
_query = _data.AsQueryable();
public override TEntity Add(TEntity item)
_data.Add(item);
return item;
public override TEntity Remove(TEntity item)
_data.Remove(item);
return item;
public override TEntity Attach(TEntity item)
_data.Add(item);
return item;
public override TEntity Create()
return Activator.CreateInstance<TEntity>();
public override TDerivedEntity Create<TDerivedEntity>()
return Activator.CreateInstance<TDerivedEntity>();
public override ObservableCollection<TEntity> Local
get
return _data;
Type IQueryable.ElementType
get return _query.ElementType;
Expression IQueryable.Expression
get return _query.Expression;
IQueryProvider IQueryable.Provider
get return _query.Provider;
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
return _data.GetEnumerator();
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
return _data.GetEnumerator();
TestSystemDbSet.cs
class TestSystemDbSet : TestDbSet<Models.System>
public override Models.System Find(params object[] keyValues)
var id = (int)keyValues.Single();
return this.SingleOrDefault(s => s.Id == id);
TestContext.cs
public class TestContext: IContext
public TestContext()
this.Systems = new TestSystemDbSet();
public DbSet<Models.System> Systems get; set;
public int SaveChangesCount get; private set;
public int SaveChanges()
this.SaveChangesCount++;
return 1;
SystemServiceTests.cs
public class SystemServiceTests
[Fact]
public void GetSystemReturnsFromContext()
var context = new TestContext();
context.Systems.Add(new Models.System Id = 1, Name = "System 1" );
context.Systems.Add(new Models.System Id = 2, Name = "System 2" );
context.Systems.Add(new Models.System Id = 3, Name = "System 3" );
var service = new SystemService(context);
var system = service.GetSystem(2);
Assert.NotNull(system);
Assert.Equal(2, system.Id);
Assert.Equal("System 2", system.Name);
SystemService.cs
public class SystemService : ISystemService
private readonly IContext _context;
public SystemService(IContext context)
_context = context;
public Models.System AddSystem(Models.System system)
var s = _context.Systems.Add(system);
_context.SaveChanges();
return s;
public Models.System GetSystem(int id)
return _context.Systems.Find(id);
ISystemService.cs
public interface ISystemService
Models.System AddSystem(Models.System system);
Models.System GetSystem(int id);
【讨论】:
【参考方案2】:.Find()
正在返回 null
,因为这是 System
的默认值。该集合不包含 ID 为 id
的项目。
.Find()
是List
的一个方法。
我建议你使用LINQ的FirstOrDefault()
原因是,您可以通过返回 IQueryable
来使用延迟加载
【讨论】:
谢谢,我会试试的,但 Find() 不会先查询本地上下文,只有在找不到任何内容时才访问数据库? 看起来无法直接实例化 DbSet,因为它的构造函数被标记为受保护。 好的,我已经更新了我的答案。Find()
只是查询内存中的列表。我推荐使用FirstOrDefault()
我可以使用FirstOrDefalut()
,但在这种情况下我真的很想使用Find()
,因为它通过在不必查询数据库时引入了效率。有什么方法可以测试Find()
什么时候使用?
问题不在于LINQ
与其他东西,它正在测试框架(实体框架)以确保其正常工作。在这种情况下,Find()
实际上是 DbSet 的成员,它实现了IDbSet<TEntity>
。我正在寻找一种模拟 DbSet 的方法,使 Find()
可以像在生产系统中一样工作。但是,我不确定这是否可能。以上是关于测试实体框架查找方法的主要内容,如果未能解决你的问题,请参考以下文章