Подтвердить что ты не робот

Unit test error: эта функция может быть вызвана только из LINQ to Entities

Я кодирую интернет-приложение MVC 5, и у меня есть выражение следующим образом:

public Expression<Func<Account, bool>> IsExpiresDateTimeLessThanMinimumDaysLeftInFreeTrialSubscription(int minimumDaysLeftInSubscriptionForEmail)
{
    return Account => System.Data.Entity.DbFunctions.DiffHours(Account.freeTrialEndDate, DateTime.UtcNow) < minimumDaysLeftInSubscriptionForEmail;
}

При извлечении данных из базы данных вышеуказанное выражение выполняется правильно. Однако при написании unit test, который использует указанное выше выражение, я получаю следующую ошибку:

Эта функция может быть вызвана только из LINQ to Entities

Я понимаю, что это потому, что функция System.Data.Entity.DbFunctions.DiffHours преобразует expression в код, который может понять только система баз данных.

Из-за вышеизложенного, возможно ли unit test указанное выше выражение при использовании mock-репозитория, который использует List, а не a DbSet? Если нет, как я должен unit test использовать любой код, который использует expression? Возможно ли unit test expression?

Спасибо заранее.

4b9b3361

Ответ 1

Когда EF генерирует код SQL, единственное, что имеет значение, это атрибут System.Data.Entity.DbFunction. Тело метода генерирует исключение, но никогда не вызывается при использовании реальной базы данных.

Чтобы проверить это, вы можете создать свой собственный метод с атрибутом DbFunction и реализацией. При работе с базой данных EF генерирует SQL и игнорирует ваш код. При запуске unit test ваш код будет выполнен.

Ваш метод будет выглядеть примерно так:

public static class TestableDbFunctions
{
    [System.Data.Entity.DbFunction("Edm", "DiffHours")]
    public static int? DiffHours(DateTime? dateValue1, DateTime? dateValue2)
    {
        if (!dateValue1.HasValue || !dateValue2.HasValue)
            return null;

        return (int)((dateValue2.Value - dateValue1.Value).TotalHours);
    }
}

Код сравнения, например, вы должны быть уверены, что это соответствует поведению SQL, иначе ваши тесты не будут действительны.

Как только вы это сделаете, просто измените свой код, чтобы использовать новый метод:

return Account => TestableDbFunctions.DiffHours(Account.freeTrialEndDate, DateTime.UtcNow) < minimumDaysLeftInSubscriptionForEmail;

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