Unit testing 如何模拟ObjectContext或ObjectQuery<;T>;在实体框架中?
如何在实体框架中模拟ObjectContext或ObjectQuery?基本模拟框架只能为接口和抽象类创建模拟(但只能为抽象/虚拟方法创建模拟) 由于ObjectContext既不是抽象的,也不是接口,因此模拟它并不容易。但是,由于具体模型容器是作为分部类生成的(如果使用设计器),因此可以将所需的方法/属性从中提取到接口。在您的代码中,您可以只使用接口,以后可以对其进行模拟 使用ObjectQuery更容易一点,因为它有一个基本接口(例如IQueryable),基本上包含您通常需要(以及LINQ所需)的所有必要操作。因此,您应该在业务逻辑中公开IQueryable而不是ObjectQuery,并且可以为该接口创建mock 另一种方法是将所有与数据访问相关的逻辑隐藏到一个单独的层中(使用最少的逻辑),使用集成测试测试该层,并模拟它以能够对其他层进行单元测试 有一些工具(我只知道)使用.NET的分析钩子来生成模拟。这些工具不限于模拟接口或抽象类,但通过它们,您可以模拟基本上任何东西,包括非虚拟和静态方法。有了这样一个工具,您不需要为了允许模拟而更改业务逻辑 尽管这种方法有时很有用,但您必须知道,提取接口依赖项(IoC)不仅有助于模拟,而且还可以减少组件之间的依赖项,这还有其他好处Unit testing 如何模拟ObjectContext或ObjectQuery<;T>;在实体框架中?,unit-testing,entity-framework,.net-3.5,mocking,Unit Testing,Entity Framework,.net 3.5,Mocking,如何在实体框架中模拟ObjectContext或ObjectQuery?基本模拟框架只能为接口和抽象类创建模拟(但只能为抽象/虚拟方法创建模拟) 由于ObjectContext既不是抽象的,也不是接口,因此模拟它并不容易。但是,由于具体模型容器是作为分部类生成的(如果使用设计器),因此可以将所需的方法/属性从中提取到接口。在您的代码中,您可以只使用接口,以后可以对其进行模拟 使用ObjectQuery更容易一点,因为它有一个基本接口(例如IQueryable),基本上包含您通常需要(以及LINQ
就个人而言,我喜欢免费软件工具中最好的,但我们也使用,这也是一个很棒的产品(但你必须为此付费)。为什么我们不能创建实际的上下文对象以用于测试?因为我们不希望我们的测试影响生产数据库,所以我们总是可以指定一个指向测试数据库的连接字符串。在运行每个测试之前,构造一个新的上下文,添加测试中需要的数据,继续进行单元测试,然后在测试清理部分删除测试期间创建的所有记录。这里唯一的副作用是,自动增量ID将在测试数据库中用完,但既然它是一个测试数据库,谁在乎呢 我知道关于这个问题的大多数答案都建议使用DI/IoC设计为数据上下文等创建接口。但我使用实体框架的原因正是为了不为我的数据库连接、对象模型和简单CRUD事务编写任何接口。为我的数据对象编写模拟接口,并编写复杂的可查询对象以支持LINQ,这违背了依赖经过高度测试和可靠的对象的目的 这种单元测试模式并不新鲜——已经使用它很长时间了,而且效果很好。正如.NET提供EF一样,RoR提供对象,每个单元测试创建它需要的对象,继续测试,然后删除所有构造的记录 如何为测试环境指定连接字符串?由于所有测试都在它们自己的专用测试项目中,因此为测试数据库添加一个带有连接字符串的新
App.Config
文件就足够了
想想看,这能帮你减轻多少头痛和疼痛 我同意其他人的看法,你不可能真正地模仿上下文。您应该使用EF DbContext,因为您可以模拟底层的DbSet。有很多文章介绍了如何做到这一点。所以我不会写怎么做。然而,如果您绝对必须使用ObjectContext(出于某种原因),并且希望对其进行单元测试,则可以使用InMemory数据库 首先安装这个Nuget包:Effort(实体框架Fake ObjectContext实现工具),它使用NMemory作为数据库。安装efforce.EF6软件包: PM>安装包efforce.EF6
这就是内存数据库中的对象。除非您告诉我们您要解决的问题,否则很难给您一个好的答案。你到底想测试什么?这里似乎已经给出了答案:太棒了,我花了这么长时间试图模拟ObjectQuery,但从来没有想过在我的存储库中将ObjectQuery替换为IQueryable,当我读到这篇文章时,我真的打了我的额头……是的。。。只需确保在抽象中不要降低IEnumerable级别,因为这样LINQ就开始表现出不同的行为(查询将在内存中执行,而不是由db服务器执行)。在某些情况下,我确信您建议的方法是完全合适的。关于为什么要模拟:1-对内存中的数据运行测试可能要快得多。2-连接到外部资源会在测试中引入依赖性,从而降低测试的健壮性。正如我所说,这些事情对你来说都不是问题。
using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using Effort;
public class DbContextHelper
{
//Fake object you can drop this if you are using your own EF context
private class DbObject
{
public Guid Id { get; set; }
public string Name { get; set; }
}
//Fake EF context you can switch with you own EF context
private class FakeDbContext : DbContext
{
public FakeDbContext(DbConnection connection)
: base(connection, true) { }
public virtual DbSet<DbObject> DbObjects { get; set; }
}
private FakeDbContext _dbContext;
public DbContextHelper()
{
//In memory DB connection
DbConnection effortConnection = DbConnectionFactory.CreatePersistent("TestInstanceName");
_dbContext = new FakeDbContext(effortConnection);
}
//You can expose your context instead of the DbContext base type
public DbContext DbContext => _dbContext;
public ObjectContext ObjectContext => ((IObjectContextAdapter)_dbContext).ObjectContext;
//Method to add Fake object to the fake EF context
public void AddEntityWithState(string value, EntityState entityState)
{
DbContext.Entry(new DbObject() { Id = Guid.NewGuid(), Name = value }).State = entityState;
}
}
DbContextHelper _dbContextHelper = new DbContextHelper();
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Added);
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Modified);
var objs = _dbContextHelper.ObjectContext.GetObjectStateEntries(EntityState.Modified | EntityState.Added);