Подтвердить что ты не робот

Как обмануть ObjectContext или ObjectQuery <T> в Entity Framework?

Как обмануть ObjectContext или ObjectQuery в платформе Entity Framework?

4b9b3361

Ответ 1

Основные издевательские рамки могут создавать только mocks для интерфейсов и для абстрактных классов (но только для абстрактных/виртуальных методов).

Поскольку ObjectContext не является ни абстрактным, ни интерфейсом, его не так просто издеваться. Однако, поскольку конкретный контейнер модели генерируется как частичный класс (если вы используете конструктор), вы можете извлечь из него методы/свойства, необходимые из него, в интерфейс. В вашем коде вы можете использовать только интерфейс, который вы можете высмеять впоследствии.

С ObjectQuery это немного проще, поскольку у него есть базовый интерфейс (например, IQueryable), который в основном содержит все необходимые операции, которые вам обычно нужны (и требуются для LINQ). Таким образом, вы должны раскрывать IQueryable вместо ObjectQuery в своей бизнес-логике, и вы можете создать mock для этого интерфейса.

Другой альтернативой является скрыть всю связанную с доступом к данным логику на отдельный уровень (с минимальной логикой), протестировать этот уровень с помощью тестов интеграции и высмеять его, чтобы иметь возможность unit test других слоев.

Есть инструменты (я знаю только TypeMock), которые используют профилирующие крючки .NET для генерации макетов. Эти инструменты не ограничены макетными интерфейсами или абстрактными классами, но с их помощью вы можете издеваться над чем угодно, включая не виртуальные и статические методы. С таким инструментом вам не нужно менять свою бизнес-логику, чтобы позволить насмехаться.

Хотя этот подход иногда полезен, вы должны знать, что извлечение зависимостей для интерфейсов (IoC) не только полезно для насмешек, но также уменьшает зависимости между вашими компонентами, что имеет и другие преимущества.

Лично мне нравится Rhino.Mocks лучший из бесплатных инструментов, но мы также используем TypeMock, который также является отличным продуктом (но вам нужно заплатить за него).

Ответ 2

Почему мы не можем просто создать фактический объект контекста, который будет использоваться в наших тестах? Поскольку мы не хотим, чтобы наши тесты влияли на производственную базу данных, мы всегда можем указать строку подключения, которая указывает на тестовую базу данных. Перед запуском каждого теста создайте новый контекст, добавьте нужные данные в свой тест, перейдите к unit test, затем в разделе "Очистка теста" удалите все записи, созданные во время теста. Единственным побочным эффектом здесь было бы то, что идентификаторы автоматического инкремента будут использоваться в тестовой базе данных, но поскольку это тестовая база данных - кому это нужно?

Я знаю, что большинство ответов на этот вопрос предлагают использовать проекты DI/IoC для создания интерфейсов для контекстов данных и т.д., но причина, по которой я использую Entity Framework, - это точно не писать никаких интерфейсов для моих подключений к базе данных, объектных моделей и простых Транзакции CRUD. Чтобы написать макетные интерфейсы для моих объектов данных и написать сложные объекты с запросами для поддержки LINQ, побеждает цель полагаться на высокопрофессиональную и надежную Entity Framework.

Эта модель для модульного тестирования не нова - Ruby on Rails использует ее в течение длительного времени, и она отлично работает. Так же, как .NET предоставляет EF, RoR предоставляет объекты ActiveRecord, и каждый unit test создает необходимые ему объекты, проходит тесты, а затем удаляет все созданные записи.

Как указать строку подключения для тестовой среды? Поскольку все тесты находятся в собственном специализированном тестовом проекте, достаточно добавить новый файл App.Config со строкой подключения для тестовой базы данных.

Подумайте, сколько головной боли и боли это спасет вас.

Ответ 3

Я согласен с остальными, что вы не можете действительно Mock ObjectContext. Вы должны использовать EF DbContext, потому что вы можете издеваться над базовым DbSet. Есть довольно много сообщений, как это сделать. Поэтому я не буду писать, как это сделать. Однако, если вы обязательно должны использовать ObjectContext (по какой-либо причине) и хотите Unit test, вы можете использовать базу данных InMemory.

Сначала установите этот пакет Nuget: Effort (Entity Framework Fake ObjectContext Realization Tool), который использует NMemory в качестве базы данных. Установите пакет Effort.EF6:

PM > Install-Package Effort.EF6

using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Core.Objects;
using System.Data.Entity.Infrastructure;
using Effort;

public class DbContextHelper
{
    //Fake object you can drop this if you are using your own EF context
    private class DbObject
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    //Fake EF context you can switch with you own EF context
    private class FakeDbContext : DbContext
    {
        public FakeDbContext(DbConnection connection)
            : base(connection, true) { }

        public virtual DbSet<DbObject> DbObjects { get; set; }
    }

    private FakeDbContext _dbContext;

    public DbContextHelper()
    {
        //In memory DB connection
        DbConnection effortConnection = DbConnectionFactory.CreatePersistent("TestInstanceName");
        _dbContext = new FakeDbContext(effortConnection);
    }

    //You can expose your context instead of the DbContext base type
    public DbContext DbContext => _dbContext;

    public ObjectContext ObjectContext => ((IObjectContextAdapter)_dbContext).ObjectContext;

    //Method to add Fake object to the fake EF context
    public void AddEntityWithState(string value, EntityState entityState)
    {
        DbContext.Entry(new DbObject() { Id = Guid.NewGuid(), Name = value }).State = entityState;
    }
}

Использование:

DbContextHelper _dbContextHelper = new DbContextHelper();
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Added);
_dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Modified);

var objs = _dbContextHelper.ObjectContext.GetObjectStateEntries(EntityState.Modified | EntityState.Added);

Там у вас есть свой объект в БД памяти.