Moqing Enity Framework 6 .Include () с использованием DbSet <>

Я хотел бы дать справку по этому вопросу. Пропусти, если хочешь. В течение долгого времени я уделял пристальное внимание продолжающимся дебатам о стековом потоке и в других местах относительно тестирования кода в части, касающейся EF. В одном из лагерей говорится, что тестирование проводится непосредственно с базой данных из-за различий между Linq to Objects & Sql и реализациями. Другой говорит, что тест на насмешку.

Еще одно расхождение во взглядах - это проблема использования репозиториев или принятия того, что DbContext и DbSet уже предоставляют единицу работы и шаблон репозитория. В то время, когда я использовал EF, я пробовал каждую комбинацию мнений, высказанную этими лагерями. Независимо от того, что я сделал, EF сложно проверить.

Я был взволнован, чтобы найти, что команда EF сделалаDbSet более издевательский в EF 6. Они также предоставилидокументация о том, как макетировать DbSet, включая асинхронные методы с использованием Moq. Работая над моим последним проектом с использованием Web Api, я понял, что, если бы я мог издеваться над EF, я мог бы пропустить написание репозиториев, поскольку обычная причина их написания - сделать вещи тестируемыми. Вдохновение пришло после прочтения нескольких сообщений в блоге, таких какэто...

- Конец фона ---

Фактическая проблема заключается в том, что после примера кода, данного командой EF о том, как использовать Moq DbSet, если .Include () используется в каком-либо коде, создается исключение ArgumentNullException.

Другиесвязанный пост на SO

Вот мой интерфейс для DbContext:

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

    int SaveChanges();

    Task<int> SaveChangesAsync();

    Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Это основной объект, с которым мой контроллер имеет дело

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

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

Наконец, вот неудачный тест ...

Испытательная установка---

   [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, на которую ссылаются. Я перепробовал несколько разных вариантов создания данных для макета, но мне не повезло.

Ответы на вопрос(4)

Ваш ответ на вопрос