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;