我的Mocked DbContext使用Moq在单元测试中写入db

时间:2014-10-10 14:43:41

标签: c# entity-framework unit-testing moq

我是嘲笑和使用Moq的新手。这是我第一次尝试模拟添加功能。我有mocks设置,它适用于读取函数,但是当我尝试对add方法进行单元测试时,它会将更改保存到db。

如何模拟添加方法?

我想我必须模拟我的UnitOfWork类,它具有SaveChanges()方法。我想知道如何设置模拟来拦截对SaveChanges()的调用而不是保存到db。

这是我到目前为止的模拟

[SetUp]
    public void SetUp()
    {
        addCount = 0;

        IEnumerable<Platform> platformList = new List<Platform>(){
            new Platform() { Id = 1, Name = "Unknown"},
            new Platform() { Id =2, Name = "Amazon"},
            new Platform() { Id = 3, Name = "Prime Pantry"}
        };
        var platformData = platformList.AsQueryable();

        var mockPlatformSet = new Mock<DbSet<Platform>>();
        mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Provider).Returns(platformData.Provider);
        mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.Expression).Returns(platformData.Expression);
        mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.ElementType).Returns(platformData.ElementType);
        mockPlatformSet.As<IQueryable<Platform>>().Setup(m => m.GetEnumerator()).Returns(platformData.GetEnumerator());
        mockPlatformSet.Setup(m => m.Add(It.IsAny<Platform>())).Callback(() => addCount++);

        var mockContext = new Mock<ApplicationDbContext>(){ CallBase = true };
        mockContext.Setup(m => m.Platforms).Returns(mockPlatformSet.Object);
        mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>()));
        mockContext.Setup(m => m.Platforms.Add(It.IsAny<Platform>())).Callback(() => addCount++);


        unitOfWork = new UnitOfWork(mockContext.Object);
        platformRepo = new PlatformRepository(mockContext.Object);

        controller = new PlatformController(platformRepo, unitOfWork);
    }

添加UnitOfWork代码

public class UnitOfWork : IUnitOfWork 
{
    private readonly DbContext _context;
    private bool _isDisposed = false;

    public UnitOfWork(DbContext context)
    {
        _context = context;
    }

    public void SaveChanges()
    {
        _context.SaveChanges();
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_isDisposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        _isDisposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

}

更新了问题

我正在尝试对我Create的{​​{1}}方法进行单元测试。在此方法中,我在repostiory上调用PlatformController函数,然后调用Add SaveChanges函数。我想验证我的UnitOfWork对象是否被添加到DbSet,但是“拦截”对Platform的调用,它不会写入db。

我该怎么做?

3 个答案:

答案 0 :(得分:5)

当我将CallBase的{​​{1}}值设置为mockContextIUnitOfWork时,这似乎解决了我的单元测试写入数据库的问题。< / p>

这行代码: false

以下是我的mockContext.As<IUnitOfWork>().CallBase = false;功能

的代码
Setup

答案 1 :(得分:3)

我不确定这是你正在寻找的答案,但用工作单位抽象EF DbContext是个糟糕的主意。原因是上下文已经 是一个工作单元实现。根据{{​​3}}中的课程描述:

  

表示工作单元和存储库模式的组合   并使您能够查询数据库并将更改组合在一起   然后将作为一个单元写回商店。

删除不必要的抽象后,模拟上下文应该相当容易,特别是如果您使用最新版本的Entity Framework。

答案 2 :(得分:-1)

您在这里尝试进行功能测试,因此拥有一个功能数据库是明智的。

EF可以使用测试连接字符串在setup和teardown方法中重新创建和销毁数据库。这将为您的测试提供真实的功能测试环境,以防止模仿真实环境。

例如:

    [TestFixtureSetUp]
    public static void SetupFixture() //create database
    {
        using (var context = new XEntities())
        {
            context.Setup();
        }
    }

    [TestFixtureTearDown]
    public void TearDown() //drop database
    {
        using (var context = new XEntities())
        {
            context.Database.Delete();
        }
    }

    [SetUp]
    public void Setup() //Clear entities before each test so they are independent
    {
        using (var context = new XEntities())
        {
            foreach (var tableRow in context.Table)
            {
                context.Table.Remove(tableRow);
            }
            context.SaveChanges();
        }
    }

更改测试项目中的连接字符串以指向&#34; DbNameTest&#34;并且你在测试中使用你的XEntities类是很好的,并且很容易设置和添加测试数据来与之交互。