ASP.NET 在本地方法中测试 DbContext
ASP.NET Testing DbContext in local methods
我有一个 ASP.NET MVC 项目,并且正在按照以下方式编写我的服务:
public class UserService : IUserService
{
public void AddUser(UserModel userModel)
{
if (ModelState.IsValid){
var user = new User
{
FirstName = userModel.FirstName,
LastName = userModel.LastName,
Email = userModel.Email,
Age = userModel.Age
};
using (var context = new MyDbContext())
{
context.Users.Add(user);
}
}
}
//... More code for service
}
还有我的 DbContext class:
public class MyDbContext : DbContext
{
public MyDbContext(): base("name=mydb"){}
public DbSet<User> Users {get; set;}
//.. More domain objects.
}
这在大多数情况下运行良好,但是当我想尝试测试它时,问题就出现了。我遵循一般准则 here 对其进行模拟,但前提是 DbContext 被声明为 class 实例,而不是函数变量。我想这样做是因为它使我能够控制事务和上下文处理。有人对如何执行此操作有任何建议吗?
我的模拟是这样的:
public class UserServiceTest
{
[Fact]
public void TestAddUser()
{
var userSet = GetQueryableMockDbSet(userData.ToArray());
var context = new MyDbContext
{
Users = userSet
};
var userService = new UserService();
userService.AddUser(); // HOW DO I GET CONTEXT IN HERE?
}
private static DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
return dbSet.Object;
}
}
您需要在服务的构造函数中传递DbContext。最简单的方法是开始使用任何 IoC 框架(AutoFac、Unity、Ninject 等)。 模拟数据库最简单的方法是开始使用任何模拟库,如 Moq 和 RhinoMocks。
public class UserService : IUserService
{
private MyDbContext _context;
public UserService (MyDbContext dbContext)
{
_context = dbContext;
}
}
实现
目标的一种方法
I want to do it this way because it enables me to have control over the transaction and context disposal.
会按照
的方式做一些事情
public class UserService : IUserService {
private ContextFactory _ContextFactory;
public UserService(ContextFactory contextFactory) {
_ContextFactory = contextFactory;
}
public void AddUser(UserModel userModel) {
...
using (var context = _ContextFactory.CreateInstance()) {
context.Users.Add(user);
}
}
}
这将允许您注入 ContextFactory
的不同实例,这将使用 Mock
创建 DbContext
我肯定更喜欢 IOC 方法,但如果你想要一个快速的解决方案而不重写太多代码,你可以这样做:
public class UserService : IUserService
{
public static Func<MyDbContext> CreateContext = () => new MyDbContext();
public UserService ()
{
}
public void AddUser(UserModel userModel)
{
...
using (var context = CreateContext())
{
context.Users.Add(user);
}
}
}
您不必更改生产代码,但在您的 TestMethod 中您可以这样做
var context = new MyDbContext
{
Users = userSet
};
UserService.CreateContext = () => context;
我有一个 ASP.NET MVC 项目,并且正在按照以下方式编写我的服务:
public class UserService : IUserService
{
public void AddUser(UserModel userModel)
{
if (ModelState.IsValid){
var user = new User
{
FirstName = userModel.FirstName,
LastName = userModel.LastName,
Email = userModel.Email,
Age = userModel.Age
};
using (var context = new MyDbContext())
{
context.Users.Add(user);
}
}
}
//... More code for service
}
还有我的 DbContext class:
public class MyDbContext : DbContext
{
public MyDbContext(): base("name=mydb"){}
public DbSet<User> Users {get; set;}
//.. More domain objects.
}
这在大多数情况下运行良好,但是当我想尝试测试它时,问题就出现了。我遵循一般准则 here 对其进行模拟,但前提是 DbContext 被声明为 class 实例,而不是函数变量。我想这样做是因为它使我能够控制事务和上下文处理。有人对如何执行此操作有任何建议吗?
我的模拟是这样的:
public class UserServiceTest
{
[Fact]
public void TestAddUser()
{
var userSet = GetQueryableMockDbSet(userData.ToArray());
var context = new MyDbContext
{
Users = userSet
};
var userService = new UserService();
userService.AddUser(); // HOW DO I GET CONTEXT IN HERE?
}
private static DbSet<T> GetQueryableMockDbSet<T>(params T[] sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(queryable.GetEnumerator());
return dbSet.Object;
}
}
您需要在服务的构造函数中传递DbContext。最简单的方法是开始使用任何 IoC 框架(AutoFac、Unity、Ninject 等)。 模拟数据库最简单的方法是开始使用任何模拟库,如 Moq 和 RhinoMocks。
public class UserService : IUserService
{
private MyDbContext _context;
public UserService (MyDbContext dbContext)
{
_context = dbContext;
}
}
实现
目标的一种方法I want to do it this way because it enables me to have control over the transaction and context disposal.
会按照
的方式做一些事情public class UserService : IUserService {
private ContextFactory _ContextFactory;
public UserService(ContextFactory contextFactory) {
_ContextFactory = contextFactory;
}
public void AddUser(UserModel userModel) {
...
using (var context = _ContextFactory.CreateInstance()) {
context.Users.Add(user);
}
}
}
这将允许您注入 ContextFactory
的不同实例,这将使用 Mock
DbContext
我肯定更喜欢 IOC 方法,但如果你想要一个快速的解决方案而不重写太多代码,你可以这样做:
public class UserService : IUserService
{
public static Func<MyDbContext> CreateContext = () => new MyDbContext();
public UserService ()
{
}
public void AddUser(UserModel userModel)
{
...
using (var context = CreateContext())
{
context.Users.Add(user);
}
}
}
您不必更改生产代码,但在您的 TestMethod 中您可以这样做
var context = new MyDbContext
{
Users = userSet
};
UserService.CreateContext = () => context;