2

Here is my code:

    [TestMethod]
    public void LoginUnregisteredUserShouldFail()
    {
        Mock<IRepository<User>> _repo = new Mock<IRepository<User>>();
        UserServiceForTest target = new UserServiceForTest(_repo.Object, new HashingService());

        var unregisteredTestUser = new User() { Email = "a", Nombre = "test", Password = "test" };
        var registeredHashedTestUser = new User() { Email = "test@test.com", Nombre = "test", Password = "qUqP5cyxm6YcTAhz05Hph5gvu9M=" };
        Expression<Func<User, bool>> expression = a => a.Email == "a";
        _repo.Setup(a => a.Single(It.Is<Expression<Func<User,bool>>>(l => l.ToString() == expression.ToString()))).Returns(unregisteredTestUser);
        Assert.IsFalse(target.ValidateCredentials(unregisteredTestUser));
    }

I want to query the Single method of my repo, matching Email, and I want the result to be the specified User.

I dont know what I'm doing wrong but I always receive null.

EDIT: My implementation is the following:

    private string GetUserPasswordFromDbByUserName(string userName)
    {
        Expression<Func<User, bool>> ax = a => a.Email == userName;
        var axx = ax.ToString();
        var user = _repo.Single(ax);
        if (user != null)
            return user.Password;
        else
            return string.Empty;
    }

It receives a string userName and for some reason, the .ToString() returns 'a => (a.Email == value(Casita.Services.UserService+<>c__DisplayClass5).userName)' instead of 'a => (a.Email == "a")'. Makes no sense to me, but I'm guessing this is the reason the comparison is failing.

Benjamin
  • 59
  • 6

2 Answers2

2

Your problem is likely the equality comparison in the Is expression parameter. They are probably not the same when converted to strings so your equality comparison might be failing. The following question details how to compare [Func] delegates:

How to check if two Expression<Func<T, bool>> are the same

Nievely I'll suggest the following might work. Note this would use http://evain.net/blog/articles/2008/02/06/an-elegant-linq-to-db4o-provider-and-a-few-linq-tricks which might require adding a reference/download of db4o. I stopped research at that point.

Func<Expression, Expression, bool> eq =
                ExpressionEqualityComparer.Instance.Equals;

Expression<Func<User, bool>> expression = a => a.Email == "a";
        _repo.Setup(a => a.Single(It.Is<Expression<Func<User,bool>>>(l => eq(l,expression))).Returns(unregisteredTestUser);

At any rate the core problem is probably that comparison.

Marc Gravell's answer seems to imply using the method your using will work, but only if everything including variables in the implementation of your actual method are exactly the same and are passed in that exact same state in the call to the repository dependency.

My understanding of this is you would have to have exactly the same definition of the expression that you are using for testing and in the actual implementation this is testing.

I would try debugging and looking at each expression converted to a string (the one in your test method and the one in your implementation, and check if they are exactly the same. If they are then that theory is out the window.)

It weakens your test, but you could just check if any expression is passed in and return what you want.

Another alternative that may work for you is checking if the returns of the delegates are the same, but again this is a weaker test:

_repo.Setup(a => a.Single(It.Is<Expression<Func<User,bool>>>(l = l(unregisteredTestUser)== expression(unregisteredTestUser)))).Returns(unregisteredTestUser);
Community
  • 1
  • 1
Joshua Enfield
  • 17,642
  • 10
  • 51
  • 98
  • I get the following error: 'expression' is a 'variable' but its used like a 'method'. expression is a lambda expression I defined before doing the Setup. – Benjamin Feb 17 '12 at 18:48
  • Indeed the expressions are different. When doing a ToString() on the 'expression' defined above I get the following: 'a => (a.Email == "a")' but when doing the same conversion to the one in the implementation, the result is: 'a => (a.Email == value(Casita.Services.UserService+<>c__DisplayClass5).userName)'. Which very obviously are not the same. I guess this is what's making the evaluation fail? – Benjamin Feb 17 '12 at 19:25
  • I edited my original post, how is that possible? I don't understand why is the string userName taking the form of 'value(Casita.Services.UserService+<>c__DisplayClass5).userName'. – Benjamin Feb 17 '12 at 19:59
  • It's the way expression tree works. I am treading into territory I understand less, but I am guessing the lambda expression in the original method is forming a closure around the variable you are assigning userName to. This means the expression tree structures are different and some of this is reflected when you convert them to strings. The information there is used to refer to variables that are "captured" when using a variable in a local scope in the expression and then passing the delegate/expression to another method/class with a different scope. – Joshua Enfield Feb 17 '12 at 20:02
  • Note value(Casita.Services.UserService+<>c__DisplayClass5).userName - Thats a reference to the userName in your implementation. The expression is capturing that reference. In your test method it's a direct reference to the string that is defined *within the expression*. I'm semi-speculating here - someone else might be able to confirm this. I'm not great with the internals of expression trees and closures. The above basically means if you passed the expression off to the repository and then changed the value of original username, the expression passed to the repository would "see" that change. – Joshua Enfield Feb 17 '12 at 20:03
  • In summary, the key difference here is one expression makes use of a closure and one expression does not. Since ToString reflects some of this "internal referencing" a direct ToString comparison doesn't work. (Take this with salt, with my limited knowledge this is speculation) – Joshua Enfield Feb 17 '12 at 20:10
  • Thanks for all the help, Joshua. As of right now I'm not sure this is the way to do it. I might have to find a different way of testing this piece of code. – Benjamin Feb 17 '12 at 20:30
  • I'm marking this as an answer, even though we didn't come up with a solution, we did find the cause. – Benjamin Feb 21 '12 at 16:32
2

Yet another (weak) way to test it would be to pass the expression against a list and let Linq To Objects return what it may. Again your wouldn't get exactly the same behavior from linq to objects as you would from Linq2Sql or Linq2EF or anything else, but...

anyway here it is:

        List<User> users = new List<User>() 
        { 
            new User { Email = "a" }, 
            new User { Email = "b" } 
        };

        _repo.Setup(a => a.Single(It.IsAny<Expression<Func<User, bool>>>()))
            .Returns<Expression<Func<User, bool>>>(predicate => users.AsQueryable()
                .Where(predicate).SingleOrDefault());

With this, whatever gets passed to the Single method, would get passed to the Where method in the list...

Jaime
  • 6,736
  • 1
  • 26
  • 42