Moq: получить значение параметра предиката выражения - PullRequest
0 голосов
/ 31 марта 2020

Я новичок в использовании Moq и пытаюсь получить значение, переданное в метод Moq'd для использования в методе Returns.

Я успешно выполнил следующее.

        _repositoryMock.Setup(x => x.GetByOrderId(It.IsAny<string>()))
            .Returns((string id) => Task.FromResult(
                new Order
                {
                    Id = id
                }));

Использование в коде:

var order = _repository.GetByOrderId("123");

Вышеописанное работало нормально, и id, переданный в метод Returns, - это тот же идентификатор, который я передал в метод GetByOrderId в моем коде.

Однако я хотел бы сделать свой репозиторий более универсальным c, поэтому я хочу изменить свой GetByOrderId на FindFirstOrDefault, который принимает предикат выражения вместо идентификатора.

Использование как это:

var order = _repository.FindFirstOrDefault( o => x.Id == "123");

С изменением юнит-теста на это:

_repositoryMock.Setup(moq => moq.FindFirst(It.IsAny<Expression<Func<Order, bool>>>()))
            .Returns((Expression<Func<Order, bool>> expression) => Task.FromResult(
                new Order
                {
                    Id = // ... HOW TO GET ID LIKE THE FIRST SAMPLE?
                }));

Так как мне получить этот идентификатор? «123». Есть ли способ?

Ответы [ 2 ]

1 голос
/ 31 марта 2020

Вы можете проанализировать Expression.

Если вы введете только x => x.Id == "123" в выражении предиката, решение может быть таким простым:

mock.Setup(x => x.FindFirstOrDefault(It.IsAny<Expression<Func<Order, bool>>>()))
    .Returns<Expression<Func<Order, bool>>>(
        predicate =>
        {
            var id = (string)((ConstantExpression)(((BinaryExpression)predicate.Body).Right)).Value;
            return new Order { Id = id };
        });

Если вы также используете другие свойства, тогда вам нужен ExpressionVisitor, который поможет вам извлечь значения для каждого свойства:

class PredicatePropertyValueExpressionVisitor : ExpressionVisitor
{
    public Dictionary<string, object> PropertyValues { get; } = new Dictionary<string, object>();

    protected override Expression VisitBinary(BinaryExpression node)
    {
        if (node.Left is MemberExpression pe && pe.Member is PropertyInfo pi)
        {
            PropertyValues[pi.Name] = (node.Right as ConstantExpression).Value;
        }

        return base.VisitBinary(node);
    }
}

Тогда макет будет:

mock.Setup(x => x.FindFirstOrDefault(It.IsAny<Expression<Func<Order, bool>>>()))
    .Returns<Expression<Func<Order, bool>>>(
        predicate =>
        {
            var visitor = new PredicatePropertyValueExpressionVisitor();
            visitor.Visit(predicate);
            return new Order { Id = visitor.PropertyValues["Id"].ToString() };
        });
1 голос
/ 31 марта 2020

Я нашел обходной путь.

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

var expected = // INIT MY EXPECTED
List<Order> orders = new List<Order>();

foreach (var expectedItem in expected.Items)
{
    orders.Add(new Order
    {
        Id = expectedItem.Id,
    });
}

Затем я настроил свой макет следующим образом.

_finicityRepositoryMock.Setup(moq => moq.FindFirst(It.IsAny<Expression<Func<Order, bool>>>()))
            .Returns((Expression<Func<Order, bool>> expression) =>
            {
                var order = orders.AsQueryable().Where(expression).FirstOrDefault();

                if (order == null)
                {
                    return Task.FromResult((Order)null);
                }

                return Task.FromResult(
                    new Order
                    {
                        BorrowerID = order.Id
                    });
            });
...