Как создать Систему. Linq. Выражения. Выражение для Подобного?

Вы не можете - если модульное тестирование означает, что вы используете в памяти поддельный репозиторий и, следовательно, используете LINQ to Objects. Если вы тестировали свои запросы с помощью LINQ to Objects, вы тестировали не ваше приложение, а только свой поддельный репозиторий.

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

С другой стороны, более опасно, если у вас есть зеленый тест, но вылетает приложение или запросы, которые не возвращают те же результаты, что и ваш тест. Запросы типа ...

context.MyEntities.Where(e => MyBoolFunction(e)).ToList()

или

context.MyEntities.Select(e => new MyEntity { Name = e.Name }).ToList()

... будут отлично работать в вашем тесте, но не с LINQ to Entities в вашем приложении.

Запрос типа ...

context.MyEntities.Where(e => e.Name == "abc").ToList()

... потенциально могут возвращать другие результаты с LINQ для объектов, чем LINQ для объектов.

Вы можете проверить это и запрос в своем вопросе, только создав интеграционные тесты, использующие поставщика LINQ to Entities вашего приложения и реальную базу данных.

Редактировать

Если вы все еще хотите написать модульные тесты, я думаю, что вы должны подделать сам запрос или хотя бы выражения в запросе. Я мог бы предположить, что что-то вроде следующего кода может работать:

Создать интерфейс для выражения Where:

public interface IEntityExpressions
{
    Expression<Func<MyEntity, bool>> GetSearchByDateExpression(DateTime date);
    // maybe more expressions which use EntityFunctions or SqlFunctions
}

Создать реализацию для вашего приложения ...

public class EntityExpressions : IEntityExpressions
{
    public Expression<Func<MyEntity, bool>>
        GetSearchByDateExpression(DateTime date)
    {
       return e => EntityFunctions.TruncateTime(e.Date) == date;
       // Expression for LINQ to Entities, does not work with LINQ to Objects
    }
}

... и вторая реализация в вашем модульном тестовом проекте:

public class FakeEntityExpressions : IEntityExpressions
{
    public Expression<Func<MyEntity, bool>>
        GetSearchByDateExpression(DateTime date)
    {
        return e => e.Date.Date == date;
       // Expression for LINQ to Objects, does not work with LINQ to Entities
    }
}

В вашем классе, где вы используете запрос, создайте закрытый член этого интерфейса и два конструктора:

public class MyClass
{
    private readonly IEntityExpressions _entityExpressions;

    public MyClass()
    {
        _entityExpressions = new EntityExpressions(); // "poor man's IOC"
    }

    public MyClass(IEntityExpressions entityExpressions)
    {
        _entityExpressions = entityExpressions;
    }

    // just an example, I don't know how exactly the context of your query is
    public IQueryable<MyEntity> BuildQuery(IQueryable<MyEntity> query,
        SearchOptions searchOptions)
    {
        if (searchOptions.Date.HasValue)
            query = query.Where(_entityExpressions.GetSearchByDateExpression(
                searchOptions.Date));
        return query;
    }
}

Используйте первый (по умолчанию) конструктор в вашем приложении:

var myClass = new MyClass();
var searchOptions = new SearchOptions { Date = DateTime.Now.Date };

var query = myClass.BuildQuery(context.MyEntities, searchOptions);

var result = query.ToList(); // this is LINQ to Entities, queries database

Используйте второй конструктор с FakeEntityExpressions в своем модульном тесте:

IEntityExpressions entityExpressions = new FakeEntityExpressions();
var myClass = new MyClass(entityExpressions);
var searchOptions = new SearchOptions { Date = DateTime.Now.Date };
var fakeList = new List<MyEntity> { new MyEntity { ... }, ... };

var query = myClass.BuildQuery(fakeList.AsQueryable(), searchOptions);

var result = query.ToList(); // this is LINQ to Objects, queries in memory

Если вы используете контейнер внедрения зависимости, вы можете использовать его, вставив соответствующую реализацию if IEntityExpressions в конструктор и не нуждаясь в конструкторе по умолчанию.

Я протестировал приведенный выше пример кода, и он сработал.

15
задан Alex Angas 8 May 2015 в 00:19
поделиться

1 ответ

Что-то вроде:

static IEnumerable<T> WhereLike<T>(
        this IEnumerable<T> data,
        string propertyOrFieldName,
        string value)
{
    var param = Expression.Parameter(typeof(T), "x");
    var body = Expression.Call(
        typeof(Program).GetMethod("Like",
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public),
            Expression.PropertyOrField(param, propertyOrFieldName),
            Expression.Constant(value, typeof(string)));
    var lambda = Expression.Lambda<Func<T, bool>>(body, param);
    return data.Where(lambda.Compile());
}
static bool Like(string a, string b) {
    return a.Contains(b); // just for illustration
}

В терминах Func :

static Expression Like(Expression lhs, Expression rhs)
{
    return Expression.Call(
        typeof(Program).GetMethod("Like",
            BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
            ,lhs,rhs);
}
16
ответ дан 1 December 2019 в 03:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: