Вызов .CreateSourceQuery () для свойства навигации возвращает ноль при модульном тестировании; работает с реальной базой данных, как мне настроить тестовые данные для соответствия? - PullRequest
0 голосов
/ 29 декабря 2011

У меня есть метод, который берет мой Entity Framework Entity и преобразует его в объект DTO. В этом методе у меня есть параметры для пропуска и ограничения количества связанных элементов для возврата. С небольшими наборами данных простой запрос, подобный этому, работал хорошо:

var query = this.AccessLogs
    .Skip(skipRelated)
    .Take(takeRelated);

С большими наборами данных я обнаружил, что это на самом деле выполняло SELECT * в моей базе данных и вызвало много проблем, так как в некоторых случаях у меня миллионы связанных записей. Задав этот вопрос я изменил запрос так:

var query = this.AccessLogs
    .CreateSourceQuery()
    .OrderBy(p => p.ID)
    .Skip(skipRelated)
    .Take(takeRelated);

Теперь, хотя это исправило проблемы с производительностью, которые у меня были во время интеграционных тестов, это приводит к сбою каждого из моих модульных тестов, потому что .CreateSourceQuery() возвращает ноль, а затем мои .OrderBy() barfs с ArgumentNullException для параметра name: source .

У меня есть репозиторий, который возвращает IQueryable<T>, и у меня есть настройка внедрения зависимостей для его модульного тестирования, поэтому я настраиваю свои «тестовые» данные следующим образом. Первоначально я просто использовал List<T>, но нашел эту статью , которая использует InMemoryObjectSet<T> для тестирования. В любом случае, мой вызов .CreateSourceQuery() возвращает ноль, даже если в базовой коллекции есть данные.

IObjectSet<Parent> ret = new InMemoryObjectSet<Parent>();
var parent = new Parent();
parent.ID = 1;
parent.Name = "Name 1";
for(int i = 0; i < 5; i++)
{
    var ch = new Child();
    ch.ID = i;
    ch.ParentID = 1;
    ch.Property1 = "Name " + i.ToString();
    parent .Children.Add(ch);
}
ret.AddObject(parent);

У меня такой вопрос: как настроить тестовые данные для юнит-тестов, чтобы .CreateSourceQuery() не возвращал ноль?

1 Ответ

1 голос
/ 30 декабря 2011

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

Почему нельзя подделать в вашем случае? CreateSourceQuery - это метод EntityCollection, а сбор сущностей зависит от реального ObjectContext. В то же время EntityCollection запечатан. Этот метод также недоступен в любом общедоступном интерфейсе. Таким образом, нет способа заменить его логику обычными API модульного тестирования. Единственный вариант - использовать более продвинутую технологию, которая позволит вам перенаправить вызов метода к другому (это обеспечивается только коммерческими TypeMock Isolator и MS Moles), но это приведет к той же проблеме, что вы пытаетесь сделать в данный момент. => Тестирование предположения о коде, которым вы не владеете. Любой вид подделки имеет смысл только в том случае, если ваш тест не тестирует связанный с EF код, запросы или постоянство - эти вещи должны быть охвачены отдельными интеграционными тестами.

Как этого избежать? В вашем тестируемом классе создайте новый метод:

protected virtual IEnumerable<AcessLog> GetLogs(int skipRelated, int takeRelated)
{
     return this.AccessLogs.
                .CreateSourceQuery()
                .OrderBy(a => p.ID)
                .Skip(skipRelated)
                .Take(takeRelated);
}

Теперь в вашем тесте не используйте исходный класс, а производный класс, который каким-то образом переопределяет метод GetLogs и возвращает то, что вы ожидаете в своем тесте.

Но подождите. Я только что пропустил тестирование вашей логики внутри GetLogs, не так ли? Да, я действительно сделал. Как упомянуто выше, этот код не может быть проверен модулем. Он должен быть покрыт отдельным интеграционным тестом с использованием реальной базы данных, но у вас есть этот код, выделенный в одном методе, и вся другая логика, зависящая от этого метода, может быть подвергнута модульному тестированию путем подделки этого метода.

Это все равно не охватывает все проблемы, которые могут возникнуть у вас CreateSourceQuery. Например, что произойдет, если ваши отношения уже загружены? Или что происходит, если ваша сущность по какой-то причине отстранена? Это побочные эффекты, которые нелегко проверить.

...