Как смоделировать ObjectContext или ObjectQuery <T>в Entity Framework? - PullRequest
22 голосов
/ 06 января 2009

Как смоделировать ObjectContext или ObjectQuery в Entity Framework?

Ответы [ 3 ]

20 голосов
/ 06 января 2009

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

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

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

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

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

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

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

3 голосов
/ 06 сентября 2013

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

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

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

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

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

0 голосов
/ 17 ноября 2017

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

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

PM> Инсталляционный пакет 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);

Вот ты где у тебя есть объект в БД памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...