Как выполнить модульный тест GetNewValues (), который содержит функцию EntityFunctions.AddDays

2

Ниже образец кода работает нормально в производстве, но не может быть единицей проверены, потому что EntityFunctions.

my unit test используется InMemoryDatabase вместо реальной базы данных SQL. Я могу легко решить мою проблемы, создав представление в базе данных SQL с вычисленным столбцом myValue и newValue. Мне нравится искать способ работы unit testбез изменения моего метода и без создания нового представления SQL


public class EcaseReferralCaseRepository : Repository
{

        public class myType
        {
                public DateTime myValue;
                public DateTime newValue;
        }

        public myType GetNewValues()
        {
                return 
                        (myType)(from o in context.EcaseReferralCases
                        select new myType
                        {
                            // LINQ to Entity
                            myValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 0),
                            newValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 30)

                            // LINQ to Object
                            //myValue = o.StartDate.AddDays(0),
                            //newValue = o.StartDate.AddDays(30)

                        });
        }
}

Эта ссылка показывает хороший пример unit test EntityFunctions, я использовал этот подход для решения одной из моих трудностей unit test, но не знаю, как решить эту проблему.

  • 0
    Какой у Вас вопрос?
  • 0
    Название это мой вопрос! Как выполнить модульное тестирование метода GetNewValues ()
Показать ещё 2 комментария
Теги:
unit-testing
frameworks
entity

3 ответа

1
Лучший ответ

Вместо вызова

System.Data.Objects.EntityFunctions.AddDays

я бы добавил пользовательский интерфейс, который переадресовывает вызов этому методу, но который затем может быть изделен для целей тестирования.

  • 0
    Я сделал то же самое в: из o в myContext.myEntities Где (myExpression) выберите o.startDate. но теперь мой вопрос: от o в myContext.myEntities Выберите EntityFunctions.AddDays (o.startDate, 30). На этот раз EntityFunctions.AddDays находится в выбранном столбце, а не в операторе where.
  • 0
    Привет, 500 - Внутренняя ошибка сервера; Мне нравится ваше предложение, но я не знаю, как реализовать в своем запросе обычную часть выражения select. Можете ли вы предоставить более подробную информацию? Спасибо
Показать ещё 1 комментарий
5

Если я ошибаюсь, вы собираетесь переключить реализацию EcaseReferralCases с помощью другого IQueryable, возможно, источника запроса LINQ To Objects.

Наиболее надежным способом, вероятно, будет использование посетителя выражения для замены вызовов EntityFunctions на ваши собственные функции, совместимые с L2Objects.

Вот моя реализация:

using System;
using System.Data.Objects;
using System.Linq;
using System.Linq.Expressions;

static class EntityFunctionsFake
{
    public static DateTime? AddDays(DateTime? original, int? numberOfDays)
    {
        if (!original.HasValue || !numberOfDays.HasValue)
        {
            return null;
        }
        return original.Value.AddDays(numberOfDays.Value);
    }
}
public class EntityFunctionsFakerVisitor : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.DeclaringType == typeof(EntityFunctions))
        {
            var visitedArguments = Visit(node.Arguments).ToArray();
            return Expression.Call(typeof(EntityFunctionsFake), node.Method.Name, node.Method.GetGenericArguments(), visitedArguments);
        }

        return base.VisitMethodCall(node);
    }
}
class VisitedQueryProvider<TVisitor> : IQueryProvider
    where TVisitor : ExpressionVisitor, new()
{
    private readonly IQueryProvider _underlyingQueryProvider;
    public VisitedQueryProvider(IQueryProvider underlyingQueryProvider)
    {
        if (underlyingQueryProvider == null) throw new ArgumentNullException();
        _underlyingQueryProvider = underlyingQueryProvider;
    }

    private static Expression Visit(Expression expression)
    {
        return new TVisitor().Visit(expression);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return new VisitedQueryable<TElement, TVisitor>(_underlyingQueryProvider.CreateQuery<TElement>(Visit(expression)));
    }

    public IQueryable CreateQuery(Expression expression)
    {
        var sourceQueryable = _underlyingQueryProvider.CreateQuery(Visit(expression));
        var visitedQueryableType = typeof(VisitedQueryable<,>).MakeGenericType(
            sourceQueryable.ElementType,
            typeof(TVisitor)
            );

        return (IQueryable)Activator.CreateInstance(visitedQueryableType, sourceQueryable);
    }

    public TResult Execute<TResult>(Expression expression)
    {
        return _underlyingQueryProvider.Execute<TResult>(Visit(expression));
    }

    public object Execute(Expression expression)
    {
        return _underlyingQueryProvider.Execute(Visit(expression));
    }
}
public class VisitedQueryable<T, TExpressionVisitor> : IOrderedQueryable<T>
    where TExpressionVisitor : ExpressionVisitor, new()
{
    private readonly IQueryable<T> _underlyingQuery;
    private readonly VisitedQueryProvider<TExpressionVisitor> _queryProviderWrapper;
    public VisitedQueryable(IQueryable<T> underlyingQuery)
    {
        _underlyingQuery = underlyingQuery;
        _queryProviderWrapper = new VisitedQueryProvider<TExpressionVisitor>(underlyingQuery.Provider);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _underlyingQuery.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public Expression Expression
    {
        get { return _underlyingQuery.Expression; }
    }

    public Type ElementType
    {
        get { return _underlyingQuery.ElementType; }
    }

    public IQueryProvider Provider
    {
        get { return _queryProviderWrapper; }
    }
}

И вот пример использования:

var linq2ObjectsSource = new List<DateTime?>() { null }.AsQueryable();
var visitedSource = new VisitedQueryable<DateTime?, EntityFunctionsFakerVisitor>(linq2ObjectsSource);
var visitedQuery = visitedSource.Select(dt => EntityFunctions.AddDays(dt, 1));
var results = visitedQuery.ToList();
Assert.AreEqual(1, results.Count);
Assert.AreEqual(null, results[0]);

Таким образом, вы получаете все желаемые характеристики:

  • Разработчики могут продолжать использовать стандартный EntityFunctions, определенный Entity Framework;
  • Производственные реализации по-прежнему гарантируют получение исключений, если они не запущены в базе данных;
  • Запросы могут быть протестированы против поддельного репозитория;
  • 0
    Большое спасибо. Я попробую это в моем реальном проекте.
  • 0
    Обратите внимание, что параметр TExpressionVisitor в QueryProvider может быть причиной чрезмерного повышения квалификации в зависимости от ваших потребностей.
Показать ещё 4 комментария
0

Мне нравится внедрять ExpressionVisitor, как рекомендовал Жан Хоминал. Моя трудность заключается в том, как определить linq2ObjectsSource, visitedSource и visitQuery в моем случае. Итак, наконец, я просто создаю интерфейс для метода IQuerable GetSelectQuery (запрос IQuerable), а затем соответствующий класс в проекте Production и Test, который получен из этого интерфейса и имеет реализацию GetSelectQuery (запрос IQuerable). Он отлично работает.

public interface IEntityFunctionsExpressions
{
   IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query); 
}

в производственном проекте:

public class EntityFunctionsExpressions : IEntityFunctionsExpressions
{
    public EntityFunctionsExpressions()
    {
    }

    public IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query)
    {
        // Expression for LINQ to Entities, does not work with LINQ to Objects
        return 
                    (myType)(from o in query
                    select new myType
                    {
                        // LINQ to Entity
                        myValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 0),
                        newValue = (DateTime)System.Data.Objects.EntityFunctions.AddDays(o.StartDate, 30)

                    });
    }
}

в проекте unit test:

public class MockEntityFunctionsExpressions : IEntityFunctionsExpressions
{
    public MockEntityFunctionsExpressions()
    {
    }

    public IQuerable<myType> GetSelectQuery(IQuerable<EcaseReferralCase> query)
    {
        // Expression for LINQ to Objects, does not work with LINQ to Entities
        return 
                    (myType)(from o in query
                    select new myType
                    {
                        // LINQ to Object
                        myValue = o.StartDate.AddDays(0),
                        newValue = o.StartDate.AddDays(30)
                    });
    }
}

затем перепишите метод GetNewValues ​​():

public myType GetNewValues ​​() {       return myrepository.EntityFunctionsExpressions.GetSelectQuery(context.EcaseReferralCases);

}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню