使用DbSet的Moqing实体框架6 .include(). [英] Moqing Entity Framework 6 .Include() using DbSet<>

查看:66
本文介绍了使用DbSet的Moqing实体框架6 .include().的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想提出这个问题的背景.如果愿意,请跳过.相当长一段时间以来,我一直密切关注关于堆栈溢出和其他与EF相关的代码测试的争论.一个阵营说,由于Linq to Objects& amp;之间的差异,直接针对数据库进行测试. SQL和实现.另一个说通过嘲笑进行测试.

I'd like to give the background to this question. Skip if you like. For quite a while I've paid close attention to the on going debates on stackoverflow and elsewhere regarding testing of code as it relates to EF. One camp says, test directly against a database because of the differences between the Linq to Objects & Sql and implementations. Another says test by mocking.

另一个意见分歧是使用存储库的问题,或者接受DbContext和DbSet已经提供了工作单元和存储库模式的问题.在使用EF的时候,我尝试了这些训练营提供的各种意见.无论我做了什么,EF都很难测试.

Another split in opinion is the issue of using repositories, or accepting that DbContext and DbSet already provide a unit of work and repository pattern. In the time that I've been using EF, I've tried about every combination of opinions provided by these camps. Regardless of what I've done, EF proves to be hard to test.

我很高兴发现EF团队做了文档有关如何模拟DbSet的信息,包括使用Moq的异步方法.在从事涉及Web Api的最新项目时,我意识到,如果可以模拟EF,就可以跳过编写存储库的过程,因为编写存储库的正常原因是使事物可测试.阅读一些博客帖子(例如此.)后,灵感就来了.

I was excited to find the EF team made DbSet more mockable in EF 6. They also provided documentation on how to mock DbSet, including async methods using Moq. In working on my latest project involving Web Api I realized that if I could mock EF, I could skip writing repositories, as the normal reason for writing them is to make things testable. Inspiration came after reading a few blog posts such as this...

-后台结束---

实际的问题是,按照EF团队给出的有关如何Moq DbSet的示例代码,如果在任何代码中都使用.Include(),则会引发ArgumentNullException.

The actual problem is that following the example code given by the EF team on how to Moq DbSet, if .Include() is used in any code, an ArgumentNullException is thrown.

SO上的其他相关文章

这是我的DbContext界面:

Here is my interface for DbContext:

public interface ITubingForcesDbContext
{
    DbSet<WellEntity> Wells { get; set; }

    int SaveChanges();

    Task<int> SaveChangesAsync();

    Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

这是我的控制器处理的主要实体

This is the main entity that my controller deals with

public class WellEntity
{
    public int Id { get; set; }
    public DateTime DateUpdated { get; set; }
    public String UpdatedBy { get; set; }

    [Required]
    public string Name { get; set; }
    public string Location { get; set; }

    public virtual Company Company { get; set; }

    public virtual ICollection<GeometryItem> GeometryItems
    {
        get { return _geometryItems ?? (_geometryItems = new Collection<GeometryItem>()); }
        protected set { _geometryItems = value; }
    }
    private ICollection<GeometryItem> _geometryItems;

    public virtual ICollection<SurveyPoint> SurveyPoints
    {
        get { return _surveyPoints ?? (_surveyPoints = new Collection<SurveyPoint>()); }
        protected set { _surveyPoints = value; }
    }
    private ICollection<SurveyPoint> _surveyPoints;

    public virtual ICollection<TemperaturePoint> TemperaturePoints
    {
        get { return _temperaturePoints ?? (_temperaturePoints = new Collection<TemperaturePoint>()); }
        protected set { _temperaturePoints = value; }
    }
    private ICollection<TemperaturePoint> _temperaturePoints;
}

这里是直接使用EF DbContext的控制器

Here is the controller which directly uses an EF DbContext

 [Route("{id}")]
 public async Task<IHttpActionResult> Get(int id)
 {
        var query = await TheContext.Wells.
                                   Include(x => x.GeometryItems).
                                   Include(x => x.SurveyPoints).
                                   Include(x => x.TemperaturePoints).
                                   SingleOrDefaultAsync(x => x.Id == id);
        if (query == null)
        {
            return NotFound();
        }
        var model = ModelFactory.Create(query);
        return Ok(model);
}

最后是失败的测试...

Finally here is the failing test...

测试设置---

   [ClassInitialize]
   public static void ClassInitialize(TestContext testContest)
        {

            var well1 = new WellEntity { Name = "Well 1" };
            var well2 = new WellEntity { Name = "Well 2" };
            var well3 = new WellEntity { Name = "Well 3" };
            var well4 = new WellEntity { Name = "Well 4" };

            well1.GeometryItems.Add(new GeometryItem());
            well1.TemperaturePoints.Add(new TemperaturePoint());
            well1.SurveyPoints.Add(new SurveyPoint());

            well2.GeometryItems.Add(new GeometryItem());
            well2.TemperaturePoints.Add(new TemperaturePoint());
            well2.SurveyPoints.Add(new SurveyPoint());

            well3.GeometryItems.Add(new GeometryItem());
            well3.TemperaturePoints.Add(new TemperaturePoint());
            well3.SurveyPoints.Add(new SurveyPoint());

            well4.GeometryItems.Add(new GeometryItem());
            well4.TemperaturePoints.Add(new TemperaturePoint());
            well4.SurveyPoints.Add(new SurveyPoint());

            var wells = new List<WellEntity> { well1, well2, well3, well4 }.AsQueryable();

            var mockWells = CreateMockSet(wells);

            _mockContext = new Mock<ITubingForcesDbContext>();
            _mockContext.Setup(c => c.Wells).Returns(mockWells.Object);
   }

   private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data) where T : class
    {
        var mockSet = new Mock<DbSet<T>>();

        mockSet.As<IDbAsyncEnumerable<T>>()
            .Setup(m => m.GetAsyncEnumerator())
            .Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));

        mockSet.As<IQueryable<T>>()
               .Setup(m => m.Provider)
               .Returns(new TestDbAsyncQueryProvider<T>(data.Provider));

        mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
        mockSet.As<IQueryable<T>>().Setup(m =>m.ElementType).Returns(data.ElementType);
        mockSet.As<IQueryable<T>>().Setup(m=>m.GetEnumerator()).
        Returns(data.GetEnumerator());

        return mockSet;
   }

  [TestMethod]  
   public async Task Get_ById_ReturnsWellWithAllChildData()
    {
        // Arrange
        var controller = new WellsController(_mockContext.Object);

        // Act
        var actionResult = await controller.Get(1);

        // Assert
        var response = actionResult as OkNegotiatedContentResult<WellModel>;
        Assert.IsNotNull(response);
        Assert.IsNotNull(response.Content.GeometryItems);
        Assert.IsNotNull(response.Content.SurveyPoints);
        Assert.IsNotNull(response.Content.TemperaturePoints);
   }

TestDbAsyncQueryProvider& TestDbAsyncEnumerator直接来自引用的EF团队文档.对于如何为模拟创建数据,我尝试了几种不同的变体,但并没有带来任何运气.

TestDbAsyncQueryProvider & TestDbAsyncEnumerator come directly from the referenced EF team documentation. I've tried several different variations for how I create the data for the mock, haven't had any luck with it.

推荐答案

对于那些对如何使用 NSubstitute和Entity Framework 6+解决.Include("Foo")问题感兴趣的人,我可以通过以下方式绕过Include调用:

For anyone who stumbles upon this issue with interest on how to solve the .Include("Foo") problem with NSubstitute and Entity Framework 6+, I was able to bypass my Include calls in the following way:

var data = new List<Foo>()
{
    /* Stub data */
}.AsQueryable();

var mockSet = Substitute.For<DbSet<Foo>, IQueryable<Foo>>();
((IQueryable<Post>)mockSet).Provider.Returns(data.Provider);
((IQueryable<Post>)mockSet).Expression.Returns(data.Expression);
((IQueryable<Post>)mockSet).ElementType.Returns(data.ElementType);
((IQueryable<Post>)mockSet).GetEnumerator().Returns(data.GetEnumerator());

// The following line bypasses the Include call.
mockSet.Include(Arg.Any<string>()).Returns(mockSet);

这篇关于使用DbSet的Moqing实体框架6 .include().的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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