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

时间:2015-02-03 03:24:48

标签: linq unit-testing lambda moq mstest

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

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

我得到了预期的用户。

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

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:0)

只需在这个旧答案中添加评论(虽然非常有用的代码)

此版本的 LambdaCompare 缺少比较 UnaryExpression 的案例,这是原始帖子中的代码中包含的内容。

使用该代码代替此代码(我学到了很多东西......)