如何在Entity Framework中模拟ObjectContext或ObjectQuery?
基本的模拟框架只能为接口和抽象类(但仅限于抽象/虚方法)创建模拟。
由于ObjectContext既不是抽象类也不是接口,因此不容易进行模拟。不过,如果使用设计器,则会生成具体的模型容器作为部分类,您可以将需要的方法/属性从中提取到接口中。在您的代码中,您只需使用该接口,然后可以对其进行模拟。
使用ObjectQuery更容易一些,因为它具有一个基本接口(例如IQueryable),基本包含了通常需要的所有必要操作(以及LINQ所需的操作)。因此,您应该在业务逻辑中公开IQueryable而不是ObjectQuery,并且可以为该接口创建模拟。
另一种选择是将所有数据访问相关的逻辑隐藏到一个单独的层中(带有最小逻辑),使用集成测试来测试这个层,并模拟它以便能够对其他层进行单元测试。
有一些工具(我只知道TypeMock)使用.NET的分析钩子来生成模拟对象。这些工具不仅限于模拟接口或抽象类,而是可以模拟基本上任何东西,包括非虚拟和静态方法。有了这样的工具,您不需要更改业务逻辑以允许模拟。
尽管这种方法有时很有用,但你必须意识到将依赖关系提取到接口(IoC)不仅有助于模拟,而且还可以减少组件之间的依赖关系,从而带来其他好处。
就我个人而言,在免费软件中我最喜欢的是Rhino.Mocks,但我们也使用TypeMock,这也是一个很棒的产品(但需要付费)。
为什么我们不能直接创建用于测试的实际上下文对象?既然我们不想让测试影响生产数据库,我们总是可以指定连接字符串指向测试数据库。在运行每个测试之前,构建一个新的上下文,添加您需要在测试中使用的数据,继续进行单元测试,然后在测试清理部分删除测试期间创建的所有记录。这里的唯一副作用是自动递增的ID将在测试数据库中使用完,但由于它是一个测试数据库,谁在乎呢?
我知道大多数问题的答案提议使用DI / IoC设计来创建数据上下文等接口,但我使用Entity Framework的原因正是为了不编写任何用于我的数据库连接,对象模型和简单的CRUD事务的接口。为我的数据对象编写模拟接口并编写复杂的可查询对象以支持LINQ,这违背了依赖于经过高度测试和可靠的Entity Framework的目的。
这种单元测试的模式并不新鲜 - Ruby on Rails 已经使用了很长时间,并且它非常成功。就像.NET提供EF一样,RoR提供ActiveRecord对象,每个单元测试都创建它需要的对象,进行测试,然后删除所有构建的记录。
如何指定测试环境的连接字符串?由于所有的测试都在它们自己的专用测试项目中,因此添加一个新的 App.Config
文件,其中包含测试数据库的连接字符串就足够了。
只要想想这将为您省去多少头痛和疼痛。
我同意其他人的看法,你不能真正地嘲弄ObjectContext。你应该使用EF DbContext,因为你可以模拟底层的DbSet。有相当多的帖子介绍如何实现这一点。所以我不会写如何做到这一点。然而,如果您绝对必须使用ObjectContext(出于某种原因),并且您想对其进行单元测试,您可以使用InMemory数据库。
首先安装此Nuget包:Effort(Entity Framework伪对象上下文实现工具),该工具使用NMemory作为数据库。安装Effort.EF6包:
PM>安装程序包Effort.EF6
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);
你已经在内存数据库中拥有你的对象了。
- winforms
- combobox
- fogbugz
- java
- date
- internationalization
- asp.net
- iis
- url-rewriting
- urlrewriter
- c#
- enums
- ocaml
- haxe
- algorithm
- string
- viewstate
- .net
- c++
- c
- symbol-table
- mysql
- database
- postgresql
- licensing
- migration
- vb.net
- vb6
- declaration
- vb6-migration
- python
- psycopg2
- backup
- vmware
- virtualization
- gnu-screen
- authentication
- desktop
- excel
- xll
- cultureinfo
- regioninfo
- oracle
- client
- session
- download
- html
- virtual
- constructor
- scenarios
- perl
- full-text-search
- javascript
- ajax
- testing
- oop
- inheritance
- vim
- encapsulation
- information-hiding