Как выполнить модульное тестирование метода репозитория, реализованного с помощью Entity Framework?

У меня есть такой метод в моем слое репозитория:

public IEnumerable<User> GetActiveUsers()
{
    return dbContext.Users
           .Where(u => u.IsActive)
           .OrderBy(u => u.Name)
           .ToList();
}

Должен ли я тестировать этот метод, издеваясь над DbContext, или я должен протестировать его с реальной базой данных?

Я знаю, что это перестает быть «модульным» тестом, когда я использую реальную базу данных, но я не вижу смысла в издевательстве над DbContext для проверки методов моего репозитория, которые имеют тонкую логику и обычно просто вызывают метод EF напрямую.

И если мне нужно использовать реальную базу данных, существует ли какая-либо стандартная стратегия для заполнения тестовых данных в базе данных, чтобы тесты выполнялись независимо и не изменяли какое-либо состояние в базе данных?


person Saravana    schedule 05.04.2014    source источник
comment
Я не делал этого с Entity Framework (я использую NHibernate в качестве ORM), но когда мне нужно проверить логику базы данных, я использую sqlite с базой данных в памяти.   -  person Claudio Redi    schedule 05.04.2014


Ответы (4)


Вероятно, это не то, что вы хотите услышать, но вы не хотите насмехаться над DbContext. Я знаю, что это делалось все время, и в EF6 это даже стало проще, чем раньше. Для реализации фиктивных объектов доступно еще больше интерфейсов и виртуальных методов. Технически это не сложно.

Важно поведение.

Даже в вашем небольшом примере есть возможный подвох. Макет DbSet будет выполнять сортировку с учетом регистра. Подключенный DbSet будет получать отсортированные данные из базы данных, и многие параметры сортировки базы данных не учитывают регистр. Таким образом, модульный тест может дать результаты, отличные от интеграционного теста, даже в этом, казалось бы, незначительном случае.

Различия между in-memory и подключенным LINQ огромны. Лично я провожу интеграционные тесты только для всего, что связано с любым запросом LINQ to Entities. Слишком просто создать фиктивный граф объектов, который выглядел бы иначе, если бы его построил EF. В своих сервисных методах я иногда составляю довольно сложные запросы, возможно, с использованием Includes, возможно, намеренно пропуская null охранников, зная, что инструкция транслируется в SQL, возможно, с некоторой ленивой загрузкой или полагаясь на исправление отношений. Я должен знать о состояниях сущностей, продолжительности жизни контекста, проверке, которая срабатывает при сохранении изменений, параллелизме ... Я просто не верю зеленым тестам, когда все это издевается.

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

person Gert Arnold    schedule 05.04.2014

С другой стороны: какую дополнительную информацию вы получите, настроив базу данных и протестировав ее?

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

Вы хотите проверить, возвращают ли ваши запросы LINQ данные из источника данных. Однако эти запросы LINQ преобразуются в SQL-запросы с помощью EF — это то, с чем вы не будете беспокоиться.

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

person Jeroen Vannevel    schedule 05.04.2014

Если IEnumerable GetActiveUsers реализован (и должен) как интерфейс, а опубликованный вами код представляет собой конкретный класс, реализующий интерфейс. Затем вы обычно используете фиктивную структуру, чтобы смоделировать интерфейс, который вы реализуете, и вернуть настроенный вами набор результатов.

Как упомянул Йерун, обычно вам не нужно проводить модульное тестирование того, что делает структура сущностей. Модульный тест проверяет только вашу логику, а не логику другой библиотеки (здесь EF).

person J.W.    schedule 05.04.2014
comment
Хотя тестирование логики базы данных по определению выходит за рамки модульных тестов, на практике оно оказывается весьма удобным, учитывая, что в большинстве запросов есть некоторая логика, которая может содержать ошибки. Я лично использую sqllite для этих случаев. - person Claudio Redi; 05.04.2014
comment
Я согласен, но я не уверен, что EF против sqlite работает так же, как EF против сервера sql. Это может привести к другому результату. - person J.W.; 05.04.2014

При написании макета для вашего DBContext и выполнении ваших запросов LINQ следует помнить, что они работают с объектом макета контекста как LINQ to OBJECTs, а не LINQ to Entities... (EF6)...

Лично я бы потратил ваше время на тестирование поведения. У Джули Лерман есть серия статей на множестве сайтов и MSDN Mag, посвященная тому, как тестировать dbContext, если это необходимо.

person Warren LaFrance    schedule 02.08.2014