使用具有不同 linq 表达式的 Moq 在测试中模拟相同的方法

Mock same method in a test using Moq with different linq expressions

我有一个使用此方法的存储库

    public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, 
IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "");

我有一个方法就是使用它来让用户继续工作

var user = this.applicationUserRepository.Get(x => x.EmailAddress == userName).FirstOrDefault();

然后在方法中使用不同的表达式调用相同的方法,如下所示,以检查正在处理第一个用户的用户

var changingUser = this.applicationUserRepository.Get(x => x.Id == changingUserId).FirstOrDefault();

我正在尝试使用两个设置来模拟存储库,以便在同一测试中调用它两次。

string emailAddress = "myaddresss@test.com";
int changingUserId = 5;

    var targetUsers = new List<ApplicationUser>
        {
            new ApplicationUser { Id = 1, Password = "Testing123", EmailAddress = emailAddress }
        };

// Setup the user that will be modified to be found
var mockApplicationUserRepository = new Mock<IRepository<ApplicationUser>>();
    mockApplicationUserRepository
            .Setup(aur => aur.Get(x => x.EmailAddress == userName, null, string.Empty))
            .Returns(targetUsers.AsEnumerable());


    // Set up to query the changing user to not be found
    mockApplicationUserRepository
        .Setup(aur2 => aur2.Get(x => x.Id == changingUserId, null, string.Empty))
        .Returns(new List<ApplicationUser>().AsEnumerable());   // Empty list

即使第二次调用永远不会被命中,但为了这次测试,我想学习如何设置它。 当我运行测试第一个调用

var user = this.applicationUserRepository.Get(x => x.EmailAddress == userName).FirstOrDefault();

我得到空值

如果我将模拟更改为

It.IsAny<Expression<Func<ApplicationUser, bool>>>()

我找回了预期的用户。

我不知道如何设置这两个调用,以便它知道要使用哪个表达式。任何帮助将不胜感激。

问题是 Moq 不会比较您使用的表达式(仅引用相等性)。

使用这个 little helper class:

public static class LambdaCompare
{
    public static bool Eq<TSource, TValue>(
        Expression<Func<TSource, TValue>> x,
        Expression<Func<TSource, TValue>> y)
    {
        return ExpressionsEqual(x, y, null, null);
    }

    public static Expression<Func<Expression<Func<TSource, TValue>>, bool>> Eq<TSource, TValue>(Expression<Func<TSource, TValue>> y)
    {
        return x => ExpressionsEqual(x, y, null, null);
    }

    private static bool ExpressionsEqual(Expression x, Expression y, LambdaExpression rootX, LambdaExpression rootY)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;

        var valueX = TryCalculateConstant(x);
        var valueY = TryCalculateConstant(y);

        if (valueX.IsDefined && valueY.IsDefined)
            return ValuesEqual(valueX.Value, valueY.Value);

        if (x.NodeType != y.NodeType
            || x.Type != y.Type) return false;

        if (x is LambdaExpression)
        {
            var lx = (LambdaExpression) x;
            var ly = (LambdaExpression) y;
            var paramsX = lx.Parameters;
            var paramsY = ly.Parameters;
            return CollectionsEqual(paramsX, paramsY, lx, ly) && ExpressionsEqual(lx.Body, ly.Body, lx, ly);
        }
        if (x is MemberExpression)
        {
            var mex = (MemberExpression) x;
            var mey = (MemberExpression) y;
            return Equals(mex.Member, mey.Member) && ExpressionsEqual(mex.Expression, mey.Expression, rootX, rootY);
        }
        if (x is BinaryExpression)
        {
            var bx = (BinaryExpression) x;
            var by = (BinaryExpression) y;
            return bx.Method == @by.Method && ExpressionsEqual(bx.Left, @by.Left, rootX, rootY) &&
                   ExpressionsEqual(bx.Right, @by.Right, rootX, rootY);
        }
        if (x is ParameterExpression)
        {
            var px = (ParameterExpression) x;
            var py = (ParameterExpression) y;
            return rootX.Parameters.IndexOf(px) == rootY.Parameters.IndexOf(py);
        }
        if (x is MethodCallExpression)
        {
            var cx = (MethodCallExpression)x;
            var cy = (MethodCallExpression)y;
            return cx.Method == cy.Method
                   && ExpressionsEqual(cx.Object, cy.Object, rootX, rootY)
                   && CollectionsEqual(cx.Arguments, cy.Arguments, rootX, rootY);
        }

        throw new NotImplementedException(x.ToString());
    }

    private static bool ValuesEqual(object x, object y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (x is ICollection && y is ICollection)
            return CollectionsEqual((ICollection) x, (ICollection) y);

        return Equals(x, y);
    }

    private static ConstantValue TryCalculateConstant(Expression e)
    {
        if (e is ConstantExpression)
            return new ConstantValue(true, ((ConstantExpression) e).Value);
        if (e is MemberExpression)
        {
            var me = (MemberExpression) e;
            var parentValue = TryCalculateConstant(me.Expression);
            if (parentValue.IsDefined)
            {
                var result =
                    me.Member is FieldInfo
                        ? ((FieldInfo) me.Member).GetValue(parentValue.Value)
                        : ((PropertyInfo) me.Member).GetValue(parentValue.Value);
                return new ConstantValue(true, result);
            }
        }
        if (e is NewArrayExpression)
        {
            var ae = ((NewArrayExpression) e);
            var result = ae.Expressions.Select(TryCalculateConstant);
            if (result.All(i => i.IsDefined))
                return new ConstantValue(true, result.Select(i => i.Value).ToArray());
        }

        return default(ConstantValue);
    }

    private static bool CollectionsEqual(IEnumerable<Expression> x, IEnumerable<Expression> y, LambdaExpression rootX, LambdaExpression rootY)
    {
        return x.Count() == y.Count()
               && x.Select((e, i) => new {Expr = e, Index = i})
                   .Join(y.Select((e, i) => new { Expr = e, Index = i }),
                         o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
                   .All(o => ExpressionsEqual(o.X, o.Y, rootX, rootY));
    }

    private static bool CollectionsEqual(ICollection x, ICollection y)
    {
        return x.Count == y.Count
               && x.Cast<object>().Select((e, i) => new { Expr = e, Index = i })
                   .Join(y.Cast<object>().Select((e, i) => new { Expr = e, Index = i }),
                         o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
                   .All(o => Equals(o.X, o.Y));
    }

    private struct ConstantValue
    {
        public ConstantValue(bool isDefined, object value) : this()
        {
            IsDefined = isDefined;
            Value = value;
        }

        public bool IsDefined { get; private set; }

        public object Value { get; private set; }
    }
}

您可以像这样设置模拟:

Expression<Func<ApplicationUser, bool>> expr = x => x.EmailAddress == emailAddress;
    var mockApplicationUserRepository = new Mock<IRepository<ApplicationUser>>();
    mockApplicationUserRepository
            .Setup(aur => aur.Get(It.Is<Expression<Func<ApplicationUser, bool>>>(x => LambdaCompare.Eq(x, expr)), null, string.Empty))
            .Returns(targetUsers.AsEnumerable());

只需对这个旧答案添加评论(尽管代码非常有用)

这个版本的 LambdaCompare class 缺少一个案例来比较 UnaryExpression,原来的代码中包含的东西post。

使用那个代码而不是这个(我学到了很多东西...)