【问题标题】:Mock same method in a test using Moq with different linq expressions在使用具有不同 linq 表达式的 Moq 的测试中模拟相同的方法
【发布时间】:2015-02-03 03:24:48
【问题描述】:

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

    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>>>()

我让预期的用户回来了。

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

【问题讨论】:

    标签: linq unit-testing lambda moq mstest


    【解决方案1】:

    问题在于 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());
    

    【讨论】:

      【解决方案2】:

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

      这个版本的 LambdaCompare缺少一个比较 UnaryExpression 的案例,该案例包含在原始帖子的代码中。

      用那个代码代替这个(我学得很辛苦……)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-14
        • 1970-01-01
        • 2014-01-18
        相关资源
        最近更新 更多