Отвечая на ваш прямой вопрос
Что касается конкретного вопроса, который вы задали, я бы предложил две вещи:
Взгляните на contextx.Client.ToArray()
и посмотрите, сколько членов у вас действительно есть в этой коллекции. Может случиться так, что коллекция Client
на самом деле пуста, и в этом случае вы действительно получите null. Или может случиться так, что первый элемент в коллекции Client имеет нулевое значение для EMail
.
Как изменится поведение, если вы вызовете contextx.SaveChanges()
перед запросом коллекции Client
в DbContext? Мне любопытно посмотреть, приведет ли вызов SaveChanges
к тому, что вновь вставленное значение будет существовать в коллекции. Это действительно не должно требоваться, но может быть какое-то странное взаимодействие между Усилием и DbContext
.
EDIT: SaveChanges()
оказывается ответом.
Общие предложения по тестированию
Поскольку вы отметили этот вопрос тегом «модульное тестирование», я дам несколько общих советов по модульному тестированию, основанных на десяти годах, проведенных в качестве специалиста по модульному тестированию и тренера. Модульное тестирование — это изолированное тестирование различных небольших частей вашего приложения. Обычно это означает, что модульные тесты взаимодействуют только с несколькими классами одновременно. Это также означает, что модульные тесты не должны зависеть от внешних библиотек или зависимостей (таких как база данных). И наоборот, тест интеграции одновременно проверяет несколько частей системы и может иметь внешние зависимости от таких вещей, как базы данных.
Хотя это может показаться придиркой к терминологии, термины важны для передачи фактического назначения ваших тестов другим членам вашей команды.
В этом случае либо вы действительно хотите провести модульное тестирование какой-то части функциональности, зависящей от DbContext, либо вы пытаетесь протестировать уровень доступа к данным. Если вы пытаетесь написать изолированный модульный тест чего-то, что напрямую зависит от DbContext, вам нужно сломать зависимость от DbContext. Я объясню это ниже в разделе Разрушение зависимости от DbContext ниже. В противном случае вы действительно пытаетесь интеграционно протестировать свой DbContext, включая то, как отображаются ваши сущности. В этом случае я всегда считал, что лучше изолировать эти тесты и использовать реальную (локальную) базу данных. Вероятно, вы захотите использовать локально установленную базу данных того же типа, что и в рабочей среде. Часто SqlExpress работает просто отлично. Направьте свои тесты на экземпляр базы данных, который тесты могут полностью уничтожить. Позвольте вашим тестам удалить все существующие данные перед запуском каждого теста. Затем они могут настроить любые данные, которые им нужны, не беспокоясь о том, что существующие данные будут конфликтовать.
Разрыв зависимости от DbContext
Итак, как написать хорошие модульные тесты, если ваша бизнес-логика зависит от доступа к DbContext
? Вы этого не сделаете.
В моих приложениях, которые используют Entity Framework для сохраняемости данных, я уверен, что доступ к DbContext
содержится в отдельном проекте доступа к данным. Как правило, я создаю классы, реализующие шаблон репозитория, и этим классам разрешено зависеть от DbContext
. Итак, в этом случае я бы создал ClientRepository
, реализующий интерфейс IClientRepository
. Интерфейс будет выглядеть примерно так:
public interface IClientRepository {
Client GetClientByEMail(string email);
}
Затем любые классы, которым нужен доступ к методу, могут быть протестированы с использованием базовой заглушки/мока/что угодно. Не нужно беспокоиться о том, чтобы высмеять DbContext
. Ваш уровень доступа к данным содержится, и вы можете тщательно протестировать его, используя реальную базу данных. Некоторые предложения о том, как протестировать уровень доступа к данным, см. выше.
В качестве дополнительного преимущества реализация этого интерфейса определяет, что значит найти Client
по адресу электронной почты в одном унифицированном месте. Интерфейс IClientRepository
позволяет быстро ответить на вопрос: "Как нам запрашивать Client
объектов в нашей системе?"
Принятие зависимости от DbContext
— это примерно тот же масштаб проблемы тестирования, что и предоставление классам предметной области зависимости от строки подключения и повсеместное наличие кода ADO.Net. Это означает, что вам нужно создать реальное хранилище данных (даже с поддельной базой данных) с реальными данными в нем. Но если вы поместите свой доступ к DbContext
в определенную сборку доступа к данным, вы обнаружите, что ваши модульные тесты писать намного проще.
Что касается организации проекта, я обычно разрешаю своему проекту доступа к данным только ссылаться на Entity Framework. У меня будет отдельный основной проект, в котором я определяю сущности. Я также определю интерфейсы доступа к данным в проекте Core. Затем конкретные реализации интерфейса включаются в проект доступа к данным. Большинство проектов в вашем решении могут тогда просто получить зависимость от основного проекта, и только исполняемый файл верхнего уровня или веб-проект действительно должен зависеть от проекта доступа к данным.
person
AggieEric
schedule
14.08.2016