I'm using Rhino Mocks to unit test a service method that makes the following call:
var form = Repo.GetOne<Form>(f => f.Id == formId, "FormTemplate, Event");
The Repo.GetOne method signature is:
TEntity GetOne<TEntity>(
Expression<Func<TEntity, bool>> filter = null,
string includeProperties = null)
where TEntity : class, IEntity;
So far I have only managed to do this by ignoring the Function Expression argument:
_mockRepo.Stub(
r =>
r.GetOne<Form>(
Arg<Expression<Func<Form, bool>>>.Is.Anything,
Arg<string>.Is.Equal("FormTemplate, Event")))
.Return(form);
How do I stub the Repo.GetOne method in order to set the return value when the method is called with arguments f => f.Id == formId, "FormTemplate, Event"?
When you setup Stub/Mocks, the value used for the arguments must return true when you call mockArg.Equals(runtimeArg). Your Func<> isn't going to do that, so you're best bet is to use the Arg<T>.Matches() constraint, which takes a function that, given the stub inputs, returns true if the runtime input are a match.
Unfortunately, looking at the contents of the Func<T> delegate is not straightforward. (Take a look at https://stackoverflow.com/a/17673890/682840)
var myArgChecker = new Function<Expression<Func<Form, bool>>, bool>(input =>
{
//inspect input to determine if it's a match
return true; //or false if you don't want to apply this stub
});
_mockRepo.Stub(
r =>
r.GetOne<Form>(
Arg<Expression<Func<Form, bool>>>.Matches(input => myArgChecker(input)),
Arg<string>.Is.Equal("FormTemplate, Event")))
.Return(form);
Related
I have the following method in my repository:
Task<TEntity> FirstOrDefaultAsync(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
CancellationToken cancellationToken = default);
I call it in the service in the following way:
var itemToUpdate = await _unitOfWork.ItemsRepository
.FirstOrDefaultAsync(x => x.Id == itemDto.Id && x.UserId == userId);
The question is how to call it in the unit test? I have a strict mock for ItemsRepository and when I call it like this, I get an Exception that it is a strict mock and there is no set up for my scenario:
ItemsRepositoryMock
.Setup(x => x.FirstOrDefaultAsync(x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0", null, CancellationToken.None))
.ReturnsAsync(firstItem);
How should I set up the mock with exactly the parameters I want?
The short answer is you cannot setup it directly in Moq. The reason is that those expressions are not equal as far as c# is concerned.
What you could do...
is write your own comparison of expressions and depending on what is allowed in that expression it can be easier or harder to do. You setup mock with
It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, x=>x => x.Id == 1 && x.UserId == "ab70793b-cec8-4eba-99f3-cbad0b1649d0")
and implement
CompareExpressions<TEntity>(Func<TEntity, bool> actual,Func<TEntity, bool> expected)
traversing expression trees and doing Assert.Fail when you detect a difference its not that difficult but if you haven't done it before it can be a challenge especially if you would like to allow equivalent and not identical expressions. Good point to start would be:
ExpressionComparison
or you could compile expression and call it with a matching element you can just do
It.Is<Expression<Func<TheEntity, bool>>(actual=>CompareExpressions(actual, new[]{
new Sample<TheEntity>{ Subject= new TheEntity{ /*...*/ }, Expected = true,
new Sample /*...*/ )
CompareExpressions(Func<TEntity, bool> actual, List<TEntity> samples)
{
var method = actual.Compile();
foreach(var sample in sampes)
Assert.AreEqual(sample.Expected, method(sample.Subject));
}
Second method is easier but has potential of false positive if you prepare not good enough samples.
I am trying to call a EntityFramework method using reflection.
The signature of the method is
DbCollectionEntry<TEntity, TElement> Collection<TElement>
(Expression <Func<TEntity, ICollection <TElement>>> navigationProperty) where TElement : class
where TEntity is a type parameter of the class. How can construct a type which which will equal Expression <Func<TEntity, ICollection <TElement>>> so I can find the right overload of the method? I tried typeof(Expression<>).MakeGenericType(new[] { typeof(Func<,>) }); which displays the same in the debugger but doesn't match by equality. Once I have the method how do I call it? I managed to find the method by excluding the one with a string parameter but I still want find out how to match the method properly. I tried invoking the method using method.MakeGenericMethod(typeof(MyType)).Invoke(context.Entry(t), new[] { lambda }); but I get the exception "Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true." Here's what I have so far
var collectionMethod = typeof(DbEntityEntry<>)
.GetMethods()
.Where(m => m.Name == "Collection")
.Select(m => new { Method = m, Params = m.GetParameters(), Args = m.GetGenericArguments() })
.Where(x => x.Args.Length == 1 && x.Params[0].ParameterType != typeof(string))
.Select(x => x.Method)
.First();
var lambda = Expression.Lambda<Func<Trade, ICollection<TradeLeg>>>(prop, param);
// this fails
var o = collectionMethod.MakeGenericMethod(typeof(TradeLeg))
.Invoke(context.Entry(t), new[] { lambda });
var collectionMethod = typeof(DbEntityEntry<>)
This line condemns you to a failure, because the method will be looked for on the generic definition, not on the constructed type you use. The method must be identified on the concrete type, not on the definition, in order to call it. Based on your code, I assume the correct expression is typeof(DbEntityEntry<Trade>).
I have the following method within a repository that I am trying to Mock:
IEnumerable<TEntity> GetAll(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "")
I've setup the following:
mockContactNumberRepository.Setup(x => x.GetAll(
It.IsAny<Expression<Func<ContactNumber, bool>>>(),
It.IsAny<Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>>(),
It.IsAny<string>()))
.Returns(new Func<Expression<Func<ContactNumber, bool>>,
IQueryable<ContactNumber>>(ex => _contactNumbers.Where(ex.Compile()).AsQueryable()));
When running the unit test I receive an error message about Parameter count mismatch. I understand this is because the Returns is only specifying the first parameter but I am unsure how to specify the further parameters.
I've found many questions that ask similar questions but not found one with multiple lambda expressions.
Any help you can give would be much appreciated.
Your GetAll method takes three arguments and returns an IEnumerable<TEntity>. The valueFunction parameter in Returns needs to have a matching signature and return type. The valueFunction parameter in your example only has two input arguments and the second argument does not match any of the argument types passed to GetAll. It should look something like this (I don't have the benefit of the compiler checking my syntax but I think what I have here should be correct):
mockContactNumberRepository
.Setup(x =>
x
.GetAll(
It.IsAny<Expression<Func<ContactNumber, bool>>>(),
It.IsAny<Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>>(),
It.IsAny<string>()))
.Returns(new Func<
Expression<Func<ContactNumber, bool>>,
Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>,
string,
IEnumerable<TEntity>>((arg1, arg2, arg3) =>
{
// arg1 is Expression<Func<ContactNumber, bool>>
// arg2 is Func<IQueryable<ContactNumber>, IOrderedQueryable<ContactNumber>>
// arg3 is string
// Do something here and return an IEnumerable<TEntity>
}));
trying to mock repository :
var expMock = new Mock<IEntityRepository>();
expMock.Setup(s => s.GetMany(It.IsAny<Expression<Func<Entity, bool>>>()))
.Returns<IQueryable<Entity>>(r =>
new List<Entity>{ new Entity() } }.AsQueryable());
but when i call it:
IEnumerable<Entity> source = _entityRepository.GetMany(w => w.IsActive);
i get an exception:
System.ArgumentException : Object of type
'System.Linq.Expressions.Expression1[System.Func2[Entity,System.Boolean]]'
cannot be converted to type 'System.Linq.IQueryable`1[Entity]'.
Simply return value which you want your mocked method to return. In your case it will be IQueryable:
expMock.Setup(s => s.GetMany(It.IsAny<Expression<Func<Entity, bool>>>()))
.Returns(new List<Entity>{ new Entity() }.AsQueryable());
Generic parameter of Returns method is a type of the argument of invoked method. Returns<IQueryable<Entity>> means that GetMany method should be invoked with parameter of type IQueryable<Entity> which is not true of course. That's why you get this exception.
Method argument is the expression, so correct mock setup should look like:
.Returns<Expression<Func<Entity, bool>>>(e =>
new List<Entity> { new Entity() }.AsQueryable());
But thus you don't need method argument for providing returned result, use code above.
Your Returns() statement is binding your Func to be returned when GetMany() is called, not evaluating the expression and returning the result. It should work if you take out the r=>. You can probably get away without the type parameter as well.
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?
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.
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... ;-)