越过实体框架BeginTransaction

Erk*_* M. 9 c# testing unit-testing entity-framework moq

我试图理解单元测试中的模拟,并将单元测试过程集成到我的项目中.所以我一直走过几个教程并重构我的代码以支持模拟,无论如何,我无法通过测试,因为我试图测试的数据库方法是使用事务,但是在创建事务时,我得到了

底层提供程序在Open上失败.

没有交易,一切正常.

我目前的代码是:

[TestMethod]
public void Test1()
{
    var mockSet = GetDbMock();
    var mockContext = new Mock<DataContext>();
    mockContext.Setup(m => m.Repository).Returns(mockSet.Object);

    var service = new MyService(mockContext.Object);
    service.SaveRepository(GetRepositoryData().First());
    mockSet.Verify(m => m.Remove(It.IsAny<Repository>()), Times.Once());
    mockSet.Verify(m => m.Add(It.IsAny<Repository>()), Times.Once());
    mockContext.Verify(m => m.SaveChanges(), Times.Once());
}

// gets the DbSet mock with one existing item
private Mock<DbSet<Repository>> GetDbMock()
{
    var data = GetRepositoryData();
    var mockSet = new Mock<DbSet<Repository>>();

    mockSet.As<IQueryable<Repository>>().Setup(m => m.Provider).Returns(data.Provider);
    // skipped for brevity
    return mockSet;
}
Run Code Online (Sandbox Code Playgroud)

被测代码:

private readonly DataContext _context;
public MyService(DataContext ctx)
{
    _context = ctx;
}

public void SaveRepositories(Repository repo)
{
    using (_context)
    {
        // Here the transaction creation fails
        using (var transaction = _context.Database.BeginTransaction())
        {
            DeleteExistingEntries(repo.Id);
            AddRepositories(repo);
            _context.SaveChanges();
            transaction.Commit();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我也试图模仿交易部分:

var mockTransaction = new Mock<DbContextTransaction>();
mockContext.Setup(x => x.Database.BeginTransaction()).Returns(mockTransaction.Object);
Run Code Online (Sandbox Code Playgroud)

但这不起作用,失败了:

非虚拟(在VB中可覆盖)成员的无效设置:conn => conn.Database.BeginTransaction()

任何想法如何解决这个问题?

Aas*_*set 5

正如第二条错误消息所示,Moq无法模拟非虚方法或属性,因此这种方法不起作用.我建议使用适配器模式来解决这个问题.我们的想法是创建一个适配器(一个实现某个接口的包装类),它与之交互DataContext,并通过该接口执行所有数据库活动.然后,您可以改为模拟界面.

public interface IDataContext {
    DbSet<Repository> Repository { get; }
    DbContextTransaction BeginTransaction();
}

public class DataContextAdapter {
    private readonly DataContext _dataContext;

    public DataContextAdapter(DataContext dataContext) {
        _dataContext = dataContext;
    }

    public DbSet<Repository> Repository { get { return _dataContext.Repository; } }

    public DbContextTransaction BeginTransaction() {
        return _dataContext.Database.BeginTransaction();
    }
}
Run Code Online (Sandbox Code Playgroud)

以前DataContext直接使用的所有代码现在应该使用a IDataContext,这应该是DataContextAdapter程序运行时的一个,但在测试中,您可以轻松地进行模拟IDataContext.这应该使模拟方式更简单,因为您可以设计IDataContextDataContextAdapter隐藏实际的一些复杂性DataContext.