过去实体框架BeginTransaction [英] Getting past entity framework BeginTransaction

查看:223
本文介绍了过去实体框架BeginTransaction的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在单元测试中嘲笑,并将单元测试流程整合到我的项目中。所以我一直走几个教程,并重构我的代码来支持嘲笑,无论如何,我无法通过测试,因为我试图测试的DB方法是使用一个事务,但是当创建一个事务时,我得到


底层提供商在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());
}

//获取一个现有项的DbSet模拟
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);
//为简洁而跳过
return mockSet;
}

测试代码:



私人readonly DataContext _context;

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

public void SaveRepositories(Repository repo)
{
using(_context)
{
//这里的事务创建失败
using(var transaction = _context.Database.BeginTransaction())
{
DeleteExistingEntries(repo.Id);
AddRepositories(repo);
_context.SaveChanges();
transaction.Commit();
}
}
}

我试图嘲笑交易部分:

  var mockTransaction = new Mock< DbContextTransaction>(); 
mockContext.Setup(x => x.Database.BeginTransaction())。Returns(mockTransaction.Object);

但这不工作,失败:




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


任何想法如何解决?

解决方案

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

  public interface IDataContext {
DbSet< 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();
}
}

以前使用 DataContext 直接现在应该使用一个 IDataContext ,它应该是一个 DataContextAdapter 该程序正在运行,但在测试中,您可以轻松地模拟 IDataContext 。这应该使嘲笑的方式更简单,因为您可以设计 IDataContext DataContextAdapter 来隐藏实际的一些复杂性 DataContext


I am trying to make sense of mocking in unit testing and to integrate the unit testing process to my project. So I have been walking thru several tutorials and refactoring my code to support mocking, anyway, I am unable to pass the tests, because the DB method I am trying to test is using a transaction, but when creating a transaction, I get

The underlying provider failed on Open.

Without transaction everything works just fine.

The code I currently have is:

[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;
}

Code under test:

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();
        }
    }
}

I was trying to mock the transaction part as well:

var mockTransaction = new Mock<DbContextTransaction>();
mockContext.Setup(x => x.Database.BeginTransaction()).Returns(mockTransaction.Object);

but this is not working, failing with:

Invalid setup on a non-virtual (overridable in VB) member: conn => conn.Database.BeginTransaction()

Any ideas how to solve this?

解决方案

As the second error message says, Moq can't mock non-virtual methods or properties, so this approach won't work. I suggest using the Adapter pattern to work around this. The idea is to create an adapter (a wrapper class that implements some interface) that interacts with the DataContext, and to perform all database activity through that interface. Then, you can mock the interface instead.

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();
    }
}

All of your code that previously used the DataContext directly should now use an IDataContext, which should be a DataContextAdapter when the program is running, but in a test, you can easily mock IDataContext. This should make the mocking way simpler too because you can design IDataContext and DataContextAdapter to hide some of the complexities of the actual DataContext.

这篇关于过去实体框架BeginTransaction的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆