簡體   English   中英

Moq:獲取表達式謂詞的參數值

[英]Moq: Get parameter value of expression predicate

我是使用 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");

以上工作正常,傳遞給 Returns 方法的id與我在代碼中傳遞給 GetByOrderId 方法的 ID 相同。

但是,我想讓我的存儲庫更通用,所以我想將我的 GetByOrderId 更改為 FindFirstOrDefault,它采用表達式謂詞而不是 ID。

用法如下:

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?
                }));

那么我怎樣才能獲得那個ID呢? “123”。 有什么辦法嗎?

我找到了解決辦法。

我只是設置了一個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,
    });
}

然后我像這樣設置我的 Mock。

_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
                    });
            });

您可以分析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() };
        });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM