Как издеваться над LINQ для сущностей помощников, таких как " SqlFunctions.StringConvert ()'
Я использую EF 4 и пытаюсь юнит-тестировать следующую строку, используя Moq:
var convertError = models
.Where(x => SqlFunctions.StringConvert((decimal?) (x.convert ?? 0)) == "0")
.Any();
И похоже, что SqlFunctions.StringConvert() бросит, если обнаружит, что контекст издевается.
Это дает ошибку, говоря:
Эта функция может быть вызвана только из LINQ в Entities
Можно ли сказать SqlFunctions.StringConvert, чтобы вернуть макет объекта, чтобы я мог избавиться от этой ошибки?
4 ответов:
Нет это невозможно, так как реализация функции выглядит следующим образом:
[EdmFunction("SqlServer", "STR")] public static string StringConvert(decimal? number, int? length) { throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall); }Вы не можете использовать Moq для подделки этой функции. Вам нужен более мощный фреймворк mocking, который позволит вам заменить вызов статической функции-вероятно, Microsoft Fakes, Typemock Isolator или JustMock.
Или вам нужно подумать о своем подходе к тестированию, потому что издевательство над контекстом-это неправильная идея. Вместо этого вы должны иметь что-то вроде:
var convertError = myQueryProvider.ConvertQuery(x.convert);Где
queryProviderбудет ваш насмешливый тип скрытие запроса. Запрос-это логика, связанная с базой данных, и он должен быть протестирован против реальной базы данных. Код вокруг вашего запроса - это логика вашего приложения, и он должен быть модульно протестирован - лучшее решение для правильного тестирования их обоих-просто разделить их через некоторый интерфейс (поставщик запросов в этом случае, но люди часто идут с полным конкретным репозиторием). Этот принцип исходит из разделения забот-выполнение запроса является отдельной заботой, поэтому он помещается в свой собственный метод, который является тестировался отдельно.
Что я сделал, так это предоставил свои собственные реализации DbFunctions, такие что LINQ to Objects в модульном тесте использует простую реализацию .NET, а LINQ To EF во время выполнения использует DbFunctionAttribute таким же образом.Данные.Сущность.DbFunctions будет. Я думал о том, чтобы издеваться над DbFunctions, но эй, реализации LINQ to Objects полезны и прекрасно работают. Вот пример:
public static class DbFunctions { [DbFunction("Edm", "AddMinutes")] public static TimeSpan? AddMinutes(TimeSpan? timeValue, int? addValue) { return timeValue == null ? (TimeSpan?)null : timeValue.Value.Add(new TimeSpan(0, addValue.Value, 0)); } }
Вы можете издеваться над EdmFunctions, и я сделал это с помощью NSubstitute (который также не поддерживает издевательства над статическими функциями). Фокус в том, чтобы обернуть DbContext в интерфейс. Затем добавьте свою статическую функцию EdmFunction в статический класс и создайте метод расширения для своего контекста в статическом классе, чтобы вызвать этот метод. Например
public static class EdmxExtensions { [EdmFunction("SqlServer", "STR")] public static string StringConvert(decimal? number, int? length) { throw EntityUtil.NotSupported(Strings.ELinq_EdmFunctionDirectCall); } public static IQueryable<Person> MyFunction(this IDbContext context, decimal? number, int? length) { context.Person.Where(s => StringConvert(s.personId, number, length); }Тогда Вы сможете издеваться над MyFunction, так как это метод, доступный интерфейсу, и EntityFramework не сердится, когда вы пытаетесь вызвать его.
Я не пробовал этого с Moq, но вы можете сделать это аналогичным образом.
Другой подход вы можете написать свой собственный метод, который имеет те же теги атрибутов и сигнатуру метода, а затем фактически реализовать цели модульного тестирования метода вместо создания исключения. Entity Framework игнорирует код функции, поэтому она никогда не вызовет его.
Comments