Effort-FirstOrDefault возвращает значение null, когда Faking Database


Вопрос

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

Однако я пытаюсь получить электронную почту пользователя после того, как я специально добавил его в базу данных в базе данных, создав Effort, вот код

MyContext contextx = new MyContext(Effort.DbConnectionFactory.CreateTransient());

var client = new Client
{
    ClientId = 2,
    PersonId = 3,
    Person = new Person
    {
        PersonId = 3,
        EMail = "xxxxx@gmail.com"
    }
};
contextx.Client.Add(client); //<-- client got added, I checked it and is there

var email = contextx.Client.Select(c => c.Person.EMail).FirstOrDefault(); 

В последней строке выше я не могу заставить ее вернуть адрес xxxx@gmail.com, но он всегда возвращает null.

Есть идеи?

Принятый ответ

Ответ на ваш прямой вопрос

По конкретному вопросу, который вы задали, я бы предложил две вещи:

  1. Взгляните на contextx.Client.ToArray() и посмотрите, сколько членов вы действительно имеете в этой коллекции. Возможно, коллекция Client фактически пуста, и в этом случае вы действительно получите нуль. Или, может быть, первый элемент в коллекции клиента имеет значение null для EMail .

  2. Как изменяется поведение, если вы вызываете contextx.SaveChanges() прежде чем запрашивать коллекцию Client в DbContext? Мне любопытно узнать, вызывает ли вызов SaveChanges новое значение в коллекции. Это действительно не требуется, но может быть какое-то странное взаимодействие между Effort и DbContext .

EDIT: SaveChanges() оказывается ответом.

Общие рекомендации по тестированию

Поскольку вы добавили этот вопрос в тег «unit-testing», я предлагаю некоторые рекомендации по единому тестированию на основе моих десяти лет, проведенных в качестве специалиста по тестированию и тренера. Единичное тестирование - это тестирование различных небольших частей вашего приложения изолированно. Обычно это означает, что модульные тесты взаимодействуют только с несколькими классами одновременно. Это также означает, что модульные тесты не должны зависеть от внешних библиотек или зависимостей (например, базы данных). И наоборот, тест интеграции включает в себя больше частей системы сразу и может иметь внешние зависимости от таких вещей, как базы данных.

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

В этом случае либо вы хотите, чтобы модуль тестировал часть функциональности, которая зависит от DbContext, или вы пытаетесь протестировать свой уровень доступа к данным. Если вы пытаетесь написать изолированный единичный тест на что-то, что напрямую зависит от DbContext, вам нужно сломать зависимость от DbContext. Я объясню это ниже в разделе «Нарушение зависимости от DbContext» ниже. В противном случае вы действительно пытаетесь интегрировать тест DbContext, в том числе и то, как отображаются ваши объекты. В этом случае мне всегда было лучше изолировать эти тесты и использовать настоящую (локальную) базу данных. Вероятно, вы захотите использовать локально установленную базу данных того же сорта, который вы используете на производстве. Часто SqlExpress работает отлично. Направьте свои тесты на экземпляр базы данных, чтобы тесты могли полностью уничтожить. Пусть ваши тесты удаляют любые существующие данные перед запуском каждого теста. Затем они могут настраивать любые данные, которые им нужны, не беспокоясь о том, что существующие данные будут конфликтовать.

Нарушение зависимости от DbContext

Итак, как вы пишете хорошие модульные тесты, когда ваша бизнес-логика зависит от доступа к DbContext ? Вы этого не сделаете.

В моих приложениях, которые используют Entity Framework для сохранения данных, я уверен, что доступ к DbContext содержится в отдельном проекте доступа к данным. Как правило, я создам классы, которые реализуют шаблон репозитория, и этим классам разрешено зависеть от DbContext . Таким образом, в этом случае я бы создал ClientRepository который реализует интерфейс IClientRepository . Интерфейс будет выглядеть примерно так:

public interface IClientRepository {

    Client GetClientByEMail(string email);

}

Затем любые классы, которые нуждаются в доступе к методу, могут быть подвергнуты блочной проверке с использованием основного файла stub / mock / whatever. Ничто не должно волноваться насчет насмешек DbContext . Ваш уровень доступа к данным содержится, и вы можете тщательно его протестировать с использованием реальной базы данных. Некоторые рекомендации по тестированию уровня доступа к данным см. Выше.

В качестве дополнительного преимущества реализация этого интерфейса определяет, что означает поиск Client по электронной почте в едином унифицированном месте. Интерфейс IClientRepository позволяет быстро ответить на вопрос: «Как мы запрашиваем объекты Client в нашей системе?»

DbContext от DbContext примерно такая же, как и проблема тестирования, поскольку позволяет доменам принимать зависимость от строки подключения и везде с кодом ADO.Net. Это означает, что вам нужно создать реальное хранилище данных (даже с поддельным db) с реальными данными в нем. Но если вы будете иметь доступ к DbContext в конкретной сборке доступа к данным, вы обнаружите, что ваши модульные тесты намного проще писать.

Что касается организации проекта, я обычно разрешаю только моему проекту доступа к данным ссылаться на Entity Framework. У меня будет отдельный основной проект, в котором я определяю сущности. Я также определяю интерфейсы доступа к данным в проекте Core. Затем конкретные реализации интерфейса попадают в проект доступа к данным. Большинство проектов в вашем решении могут просто зависеть от проекта Core, и только проект исполнительного или верхнего уровня на самом высоком уровне действительно должен зависеть от проекта доступа к данным.





Лицензировано согласно: CC-BY-SA
Не связан с Stack Overflow
Является ли этот КБ законным? Да, узнайте, почему