Модульное тестирование DbContext - PullRequest
50 голосов
/ 20 июля 2011

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

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

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

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

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

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

Ответы [ 5 ]

36 голосов
/ 21 июля 2011

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

Вы упомянули 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();
        }
    }
}

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

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

13 голосов
/ 29 января 2013

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

http://effort.codeplex.com

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

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

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

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

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

12 голосов
/ 12 августа 2012

Начиная с EF 4.3, вы можете модульно протестировать свой код , вставив поддельный DefaultConnectionFactory перед созданием контекста.

5 голосов
/ 18 октября 2011

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

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

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

Для тех, кто все еще ищет ответы - я написал простую библиотеку, чтобы облегчить насмешку над DbContext очень простым способом.Подробности см. В моем другом ответе на SO на аналогичный вопрос: https://stackoverflow.com/a/33716219/111438.

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

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