Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify method call with Lambda expression - Moq

Tags:

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?

like image 715
Sergi Papaseit Avatar asked Jul 11 '11 15:07

Sergi Papaseit


2 Answers

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.

like image 196
Kaleb Pederson Avatar answered Sep 21 '22 09:09

Kaleb Pederson


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:

  • how-to-test-expressions-equality
  • most-efficient-way-to-test-equality-of-lambda-expressions

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... ;-)

like image 32
Pflugs Avatar answered Sep 23 '22 09:09

Pflugs