Verify method call with Lambda expression - Moq
Asked Answered
M

2

26

I have a Unit of Work implementation with, among others, the following method:

T Single<T>(Expression<Func<T, bool>> expression) where T : class, new();

and I call it, for instance, like this:

var person = _uow.Single<Person>(p => p.FirstName == "Sergi");

How can I verify that the Single method has been called with an argument of FirstName == "Sergi"?

I've tried the following, but to no avail:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));

// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");

session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));

They all result in the folowing error:

Expected invocation on the mock at least once, but was never performed

Any ideas on how that can be done? I'm using the latest Moq from NuGet, version 4.0.10827.0

UPDATE: A Specific example

What I'm seeing is that whenever I use string literals inside the lambda, Verify works. As soon as I'm comparing variables it fails. Case in point:

// the verify
someService.GetFromType(QuestionnaireType.Objective)

session.Verify(x => x.Single<Questionnaire>(q => 
    q.Type == QuestionnaireType.Objective));


// QuestionnaireType.Objective is just a constant:
const string Objective = "objective";


// the method where it's called (FAILS):
public Questionnaire GetFromType(string type)
{
    // this will fail the Verify
    var questionnaire = _session
        .Single<Questionnaire>(q => q.Type == type);
}

// the method where it's called (PASSES):
public Questionnaire GetFromType(string type)
{
    // this will pass the Verify
    var questionnaire = _session
        .Single<Questionnaire>(q => q.Type == QuestionnaireType.Objective);
}

How come the Verify fails as soon as I use the method parameter in the lambda expression?

What would be the proper way to write this test?

Mydriatic answered 11/7, 2011 at 15:36 Comment(0)
S
18

The direct approach works just fine for me:

// direct approach 
session.Verify(x => x.Single<Person>(p => p.FirstName == "Sergi"));

The expression object doesn't return true for equivalent expressions so this will fail:

// comparing expressions
Expression<Func<Person, bool>> expression = p => p.FirstName == "Sergi");

session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => e == expression));

To understand why, run the following NUnit test:

[Test]
public void OperatorEqualEqualVerification()
{
    Expression<Func<Person, bool>> expr1 = p => p.FirstName == "Sergi";
    Expression<Func<Person, bool>> expr2 = p => p.FirstName == "Sergi";
    Assert.IsTrue(expr1.ToString() == expr2.ToString());
    Assert.IsFalse(expr1.Equals(expr2));
    Assert.IsFalse(expr1 == expr2);
    Assert.IsFalse(expr1.Body == expr2.Body);
    Assert.IsFalse(expr1.Body.Equals(expr2.Body));
}

And as the test above indicates, comparing by the expression body will also fail, but string comparison works, so this works as well:

// even their string representations!
session.Verify(x => x
    .Single(It.Is<Expression<Func<Person, bool>>>(e => 
        e.ToString() == expression.ToString()));

And here's one more style of test you can add to the arsenal that also works:

[Test]
public void CallbackVerification()
{
    Expression<Func<Person, bool>> actualExpression = null;
    var mockUow = new Mock<IUnitOfWork>();
    mockUow
        .Setup(u => u.Single<Person>(It.IsAny<Expression<Func<Person, bool>>>()))
        .Callback( (Expression<Func<Person,bool>> x) => actualExpression = x);
    var uow = mockUow.Object;
    uow.Single<Person>(p => p.FirstName == "Sergi");

    Expression<Func<Person, bool>> expectedExpression = p => p.FirstName == "Sergi";

    Assert.AreEqual(expectedExpression.ToString(), actualExpression.ToString());
}

As you have a number of test cases that fail that shouldn't, you likely have a different problem.

UPDATE: Per your update, consider the following setup and expressions:

string normal_type = "NORMAL";
// PersonConstants is a static class with NORMAL_TYPE defined as follows:
// public const string NORMAL_TYPE = "NORMAL";
Expression<Func<Person, bool>> expr1 = p => p.Type == normal_type;
Expression<Func<Person, bool>> expr2 = p => p.Type == PersonConstants.NORMAL_TYPE;

One expression references an instance variable of the containing method. The other represents an expression that references a const member of a static class. The two are different expressions, regardless of the values that may be assigned to the variables at runtime. If however, string normal_type is changed to const string normal_type then the expressions are again the same as each reference a const on the right hand side of the expression.

Stew answered 11/7, 2011 at 16:36 Comment(5)
Thanks a lot for your answer. I've updated my question based on new er... findings, for lack of a better word. Any ideas?Mydriatic
I've updated my question to address what may be your problem. It really depends on the type of QuestionnaireType.Objective. I expect that if you ToString() the two expressions you'll see they are of different types.Stew
Thanks again, it does make sense I suppose. But what would be the proper way to write this test then? I feel that in this case mocking is and impediment instead of a vehicle to clearer code...Mydriatic
+1 for the "direct approach". it works and seems like the simplest solution (which is usually the right one)Nam
+1 for the reminder about using Callback() for when you need to test what value the result of a private helper method did to a input parameterOurs
V
1

I would also like to share another approach to comparing the parameter expression to the expected expression. I searched StackOverflow for "how to compare expressions," and I was led to these articles:

I was then led to this Subversion repository for db4o.net. In one of their projects, namespace Db4objects.Db4o.Linq.Expressions, they include a class named ExpressionEqualityComparer. I was able to checkout this project from the repository, compile, build, and create a DLL to use in my own project.

With the ExpressionEqualityComparer, you can modify the Verify call to something like the following:

session.Verify(x => x .Single(It.Is<Expression<Func<Person, bool>>>(e => new ExpressionEqualityComparer().Equals(e, expression))));

Ultimately, the ExpressionEqualityComparer and the ToString() techniques both return true in this case (with the ToString most likely being faster - speed not tested). Personally, I prefer the comparer approach since I feel it is more self-documenting and better reflects your design intent (comparing the expression objects rather a string comparison of their ToString outputs).

Note: I'm still looking for a db4o.net license file in this project, but I've not modified the code in anyway, included the copyright notice, and (since the page is publicly available) I'm assuming that's enough for now... ;-)

Vanhook answered 10/6, 2014 at 18:21 Comment(1)
Personally I think MOQ should add this ExpressionComparer and use this when using Setup and VerifyMaid

© 2022 - 2024 — McMap. All rights reserved.