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

Единичное тестирование DbContext

Я изучил некоторую информацию о тех методах, которые я мог бы использовать для unit test DbContext. Я хотел бы добавить некоторые данные в памяти в контекст, чтобы мои тесты могли работать против него. Я использую подход Database-First.

Две статьи, которые я нашел наиболее полезными, были this и this. Этот подход основан на создании интерфейса IContext, который будут реализовываться как MyContext, так и FakeContext, позволяя Mock контекст.

Однако я стараюсь избегать использования репозиториев для абстрактного EF, поскольку указал несколько человек, поскольку EF 4.1 уже реализует репозиторий и единицу рабочих шаблонов через DbSet и DbContext, и я действительно хотел бы сохранить все функции, реализованные EF Team, не имея необходимости поддерживать их с помощью общего хранилища, как я уже делал в другом проекте (и это было болезненно).

Работа с IContext приведет меня к одному и тому же пути (или не так ли?).

Я думал о создании FakeContext, который наследуется от основного MyContext и, таким образом, использует DbContext под ним для запуска моих тестов без попадания в базу данных. Я не мог найти подобных реализаций, поэтому я надеюсь, что кто-то может мне помочь в этом.

Я делаю что-то неправильно, или это может привести меня к некоторым проблемам, которые я не ожидаю?

4b9b3361

Ответ 1

Задайте себе один вопрос: что вы собираетесь тестировать?

Вы упомянули FakeContext и издеваетесь над контекстом - зачем использовать оба? Это просто разные способы сделать то же самое - предоставить только тестовую реализацию контекста.

Есть еще одна большая проблема - фальсификация или издевательский контекст или набор имеет только один результат: вы больше не тестируете свой реальный код.

Простой пример:

public interface IContext : IDisposable
{
     IDbSet<MyEntity> MyEntities { get; }
}

public class MyEntity
{
    public int Id { get; set; }
    public string Path { get; set; } 
}

public class MyService
{
    private bool MyVerySpecialNetMethod(e)
    {
        return File.Exists(e.Path);
    }

    public IEnumerable<MyEntity> GetMyEntities()
    {
        using (IContext context = CreateContext())
        { 
            return context.MyEntities
                          .Where(e => MyVerySpecialNetMethod(e))
                          .Select(e)
                          .ToList();
        }
    }
}

Теперь представьте, что это у вас в SUT (система под тестированием - в случае unit test это единица = обычно метод). В тестовом коде вы предоставляете FakeContext и FakeSet, и он будет работать - у вас будет зеленый тест. Теперь в производственном коде вы предоставите другой полученный DbContext и DbSet, и вы получите исключение во время выполнения.

Почему? Поскольку, используя FakeContext, вы также изменили провайдера LINQ, а вместо LINQ to Entities вы используете LINQ to Objects, поэтому вызываете локальные методы .NET, которые не могут быть преобразованы в SQL, а также многие другие функции LINQ, которые недоступны в LINQ к сущностям! Существуют и другие проблемы, которые вы можете найти с изменением данных: ссылочная целостность, каскадные удаления и т.д. Именно поэтому я считаю, что код, связанный с контекстом /LINQ to Entities, должен быть охвачен интеграционными тестами и выполнен с реальной базой данных.

Ответ 2

Как и в EF 4.3, вы можете unit test ваш код путем ввода поддельного DefaultConnectionFactory перед созданием контекста.

Ответ 3

Я разрабатываю библиотеку с открытым исходным кодом для решения этой проблемы.

http://effort.codeplex.com

Маленький тизер:

Вам не нужно добавлять какой-либо шаблонный код, просто вызовите соответствующий API библиотеки, например:

var context = Effort.ObjectContextFactory.CreateTransient<MyContext>();

Сначала это может показаться волшебным, но созданный объект ObjectContext будет связываться с в памяти базы данных и вообще не будет разговаривать с исходной реальной базой данных, Термин "переходный" относится к жизненному циклу этой базы данных, он живет только при наличии созданного объекта ObjectContext. Одновременно созданные объекты ObjectContext обмениваются данными с выделенными экземплярами базы данных, данные не передаются по ним. Это позволяет легко писать автоматизированные тесты.

Библиотека предоставляет различные функции для настройки создания: обмениваться данными между экземплярами, устанавливать исходные данные в базе данных, создавать фальшивую базу данных на разных уровнях данных... проверить сайт для получения дополнительной информации.

Ответ 4

Entity Framework 4.1 близок к тому, чтобы быть в курсе тестов, но требует немного дополнительных усилий. Шаблон T4 предоставляет вам производный класс DbContext, который содержит свойства DbSet. Две вещи, которые, как мне кажется, вам нужно высмеять, - это объекты DbSet, которые возвращают эти свойства и их свойства и методы, используемые в производном классе DbContext. Оба могут быть достигнуты путем изменения шаблона T4.

Брент Маккендрик показал типы модификаций, которые должны быть сделаны в этом сообщении, но не модификации шаблона T4, которые могут это сделать. Примерно:

  • Преобразование свойств DbSet в производный класс DbContext в свойства IDbSet.
  • Добавить раздел, который генерирует интерфейс для производного класса DbContext, содержащего свойства IDbSet и любые другие методы (такие как SaveChanges), которые вам нужно высмеять.
  • Реализовать новый интерфейс в производном классе DbContext.

Ответ 5

Для тех, кто все еще ищет ответы - я написал простую библиотеку, чтобы облегчить насмешку над DbContext очень простым способом. Подробнее см. Мой другой ответ на SO по аналогичному вопросу: fooobar.com/info/111916/....

PS Я согласен с тем, что говорит Ladislav Mrnka, однако я думаю, что в некоторых случаях совершенно неизбежно, что вам нужно высмеивать свой DbSet и запускать модульные тесты против него. Хотя вам нужно иметь в виду, что вы не должны тестировать свои запросы LINQ, чтобы проверить, вернули ли они правильные данные. То, что интеграционные тесты более применимы.