Moq chaining expressions results in parameter count mismatch - c#

I'm attempting to mock a repository using Moq. I have found multiple questions with similar issues but none that I found were able to solve the issue I'm having.
So I'm using a repository that can be downloaded here. More specifically, the repository itself can be viewed here and the query extensions I'm using can be seen here.
This is what my tests setup looks like:
// A List<> of fakes.
this.containers = Builder<Container>.CreateListOfSize(10).Build();
// Here I'm trying to set up the mock object for the query.
this.containerRepoMock.Setup(p => p.
Query(It.IsAny<Expression<Func<Container, bool>>>())
.Include(It.IsAny<Expression<Func<Container, object>>>())
.Select())
.Returns((Expression<Func<Container, bool>> query, Expression<Func<Container, object>> include) =>
this.containers.AsQueryable().Where(query).Include(include));
// Tell the service to use the mock repository.
this.containerService = new ContainerService(mockUnitOfWork.Object);
This is the service method that I'm trying to test:
public ContainerDto GetContainerAndItsCategories(int containerId)
{
var entity = Repository
.Query(w => w.ContainerId == containerId)
.Include(c => c.Categories)
.Select();
var output = Mapper.EntityToDto(entity.SingleOrDefault());
return output;
}
Whenever i try to run this service method in my test using the mock repository it throws an exception "System.Reflection.TargetParameterCountException: Parameter count mismatch."
I have tried adding extra object arguments to the .Returns() method in the mock setup without any success. It always throws the same exception. From what I have been reading Moq is somewhat limited when it comes to testing expressions however i have seen examples where people are doing similar things with success.
Since the Include() and Query() methods return a IQueryFluent() instance instead of direct expressions i have tried to use the QueryFluent() class in the Moq return() method but haven't been able to do it successfully with the queryable(compile errors).
Any help would be greatly appreciated, really want to be able to test this properly using unit tests.
Edit - Similar questions that I've looked at
Moq + Unit Testing - System.Reflection.TargetParameterCountException: Parameter count mismatch
Moq Params TargetParameterCountException : Parameter count mismatch Exception

The issue is with this line:
this.containerRepoMock.Setup(p => p.
Query(It.IsAny<Expression<Func<Container, bool>>>())
.Include(It.IsAny<Expression<Func<Container, object>>>())
.Select())
You cannot (as far as my Moq knowledge goes) set up a group of methods in one Setup call. Furthermore, in this line:
.Returns((Expression<Func<Container, bool>> query, Expression<Func<Container, object>> include) =>
this.containers.AsQueryable().Where(query).Include(include));
You're telling Moq to expect (in the Setup call) a call to a single method with two parameters (query and include) and to pass those to the Returns lambda here. This is obviously not the case: you have calls to two one-parameter methods, and so Moq throws a parameter mismatch exception.
You'll need to break it down into its components and setup mocks for the return values. Also, since you want to make use of the Expression objects you'll need to save those off for use at the end. For example:
var containerRepoMock = new Mock<IRepositoryAsync<Container>>();
var queryFluentQueryMock = new Mock<IQueryFluent<Container>>();
var queryFluentIncludeMock = new Mock<IQueryFluent<Container>>();
Expression<Func<Container, bool>> query = null;
containerRepoMock.Setup(p => p.Query(It.IsAny<Expression<Func<Container, bool>>>()))
.Callback<Expression<Func<Container, bool>>>(q => query = q)
.Returns(queryFluentQueryMock.Object);
Expression<Func<Container, object>> include = null;
queryFluentQueryMock.Setup(p => p.Include(It.IsAny<Expression<Func<Container, object>>>()))
.Callback<Expression<Func<Container, object>>>(i => include = i)
.Returns(queryFluentIncludeMock.Object);
queryFluentIncludeMock.Setup(p => p.Select())
.Returns(containers.AsQueryable().Where(query).Include(include));
Please forgive any compilation errors, I didn't download the libraries you linked to to try this out

Related

Mocking the NHibernate JoinAlias with Moq

I have the following repository code that will return a Company object based on an ID value, searching across the standard Company table and an additional table named ExternalCompany.
public Company FindByIdJoin(long id)
{
ExternalCompany xcomp = null;
return Session.QueryOver<Company>()
.Where(p => p.ObjectId == id)
.JoinAlias(p => p.ExternalCompanies, () => xcomp, JoinType.LeftOuterJoin)
.SingleOrDefault<Company>();
}
The code returns values that I expect. However, the trouble I'm having is in writing a Moq unit test to handle the JoinAlias call.
In a simpler method, called FindById, the code is essentially the same except there is no line for JoinAlias. The unit test for this simpler method is:
[Test]
public void FindById_returns_Company_for_valid_Id()
{
// Arrange
Mock<IQueryOver<Company, Company>> mockQueryOver = new Mock<IQueryOver<Company, Company>>();
mockQueryOver.Setup(x => x.Where(It.IsAny<Expression<Func<Company, bool>>>())).Returns(mockQueryOver.Object);
mockQueryOver.Setup(x => x.SingleOrDefault()).Returns(fake_Company);
// Act
var result = _repository.FindById(fake_Company.ObjectId);
// Assert
Assert.IsNotNull(result);
mockQueryOver.VerifyAll();
}
This test works and passes without a problem (fake_Company and _repository are defined elsewhere).
The problem is trying to put a test together for the FindByIdJoin call. I have tried using an additional Setup line like this (which goes between the Where and SingleOrDefault Setup lines):
mockQueryOver.Setup(x => x.JoinAlias(It.IsAny<Expression<Func<Company>>>(), It.IsAny<Expression<Func<ExternalCompany>>>(), JoinType.LeftOuterJoin)).Returns(mockQueryOver.Object);
The system tells me that "the best overloaded method match for IQueryOver ... has some invalid arguments."
So, I tried a few other variations on the Setup, but could not find a workable pattern.
My question is: what Setup arguments will work for JoinAlias so that I can properly test the FindByIdJoin method? Thanks!
The particular overload of JoinAlias you are using is
IQueryOver<TRoot, TSubType> JoinAlias(
Expression<Func<TSubType, object>> path,
Expression<Func<object>> alias,
JoinType joinType);
So, your setup needs to match this. Based on how you setup your IQueryOver mock, the correct setup would then be
mockQueryOver.Setup(x => x.JoinAlias(
It.IsAny<Expression<Func<Company, object>>>(),
It.IsAny<Expression<Func<object>>>(),
JoinType.LeftOuterJoin))
.Returns(mockQueryOver.Object);
Shouldn't MOQ be used for behaviour tests? Such Data access tests seem to be state dependent and I think using mock objects for such a system under test is an overkill.

Passing a predicate function parameter in moq fails

I am trying to use moq to mock a function on my licence class.
The licence class has the following interface:
Licence TryGetLicence(Predicate<Licence> filter);
In my integration test I am using mef to lazy load objects. My class finds the mef loaded objects and needs to check if there are licences available. For my test I create two objects, one of which can be licenced. I want to use moq to only return a licence for this object and null for the other, just as the real class would. The problem I am having is that moq doesn’t like me passing in a predicate. I’m not sure if moq just doesn’t handle predicates in this way or am I just implementing this wrong?
Here are the lines of code I have in my test that sets up moq for the above interface:
var lic = new Licence
{
LicId = Guid.Parse("53024D4E-3A01-4489-A341-753D04748EB9"),
LicName = "test",
Count = 1,
ExpiryDate = DateTime.Now.AddDays(2)
};
var mockAgent = new Mock<ILicenceAgent>();
mockAgent.Setup(x => x.TryGetLicence (y => y.LicId == lic.LicId)) Returns(lic);
This builds but when the last line is hit it throws an Unsupported expression exception.
For other tests I have used:
mockAgent.Setup(x => x. TryGetLicence (It.IsAny<Predicate<Licence>>())).Returns(lic);
I can’t use this for my new test as it would return a valid licence for both objects I have loaded.
Can moq be used in the way I am trying to use it?
This should solve the problem:
Predicate<Licence> predicate = y => y.LicId == lic.LicId;
mockAgent.Setup(x => x.TryGetLicence (predicate)).Returns(lic);
When you create the predicate inside Setup call, lambda expression is evaluated as a part of the expression that will be passed as a parameter. We prevent that by making sure it's only a delegate on the line above.
If you have 2 licences - lic1 and lic2, you can setup mock like this:
mockAgent.Setup(x => x.TryGetLicence(It.Is<Predicate<Licence>>(/* add your specific condition for licence1 */ ))).Returns(lic1);
mockAgent.Setup(x => x.TryGetLicence(It.Is<Predicate<Licence>>(/* add your specific condition for licence2 */ ))).Returns(lic2);

How to mock results of Enumerable.Any() extension using moq

I'm using mongo driver, and trying to fake results of any to test whether Insert or Update was called based on results.
Here's piece of code I think relevant:
_context = _collection.AsQueryable();
if (_context.Any(s => s.Id == id))
{
...
after that I'm calling either _collection.Update() or _collection.Insert().
Here's what I tried so far with the unit test:
var collectionMock = new Mock<MongoCollection<Storage>>();
var queriableMock = new Mock<IQueryable<Storage>>();
queriableMock.Setup(q => Enumerable.Any(q)).Returns(() => false);
...
collectionMock.Setup(c => c.AsQueryable()).Returns(() => queriableMock.Object);
collectionMock.Setup(c => c.Save(It.IsAny<Storage>()));
I'm getting exception
"Expression references a method that does not belong to the mocked
object: q => q.Any()"
The Setup method takes a lambda that is not executed but is interpreted so that the mock can identify methods/properties of the mock object that will be called during the test and what should be returned/thrown/called back/etc.
Moq doesn't know the implementation of Enumerable.Any<T>(this T item), and therefore cannot figure out what methods or properties of T will be accessed or what they should do/return.
Therefore, in order to mock a call to Enumerable.Any, you need to identify what methods/properties of your object it, in turn, calls, and then mock those.
You can find the implementation here. Simply follow the call path and mock out everything Any needs to call.

How to Moq NHibernate extension methods?

I'm developing an application using NHibernate for the ORM, NUnit for unit testing and Ninject for my DI. I'm mocking the ISession like so:
var session = new Mock<ISession>();
With the regular non-mocked session objects I can query them with LINQ extension methods like this:
var result = Session.Query<MyEntity>();
But when I try to mock this with the following code...
session.Setup(s => s.Query<MyEntity>());
...I get a runtime "Not supported" exception:
Expression references a method that does not belong to the mocked object: s => s.Query<MyEntity>()
How can I mock basic queries like this in Moq/NHibernate?
Query<T>() is an extension method, that's why you can't mock it.
Although #Roger answer is the way to go, sometimes it's useful to have specific tests. You can start investigating what Query<T>() method does - either by reading the NHibernate code, or using your own tests, and set the appropriate methods on ISession.
Warning: creating such a setup will make your test very fragile, and it will break, if the internal implementation of NHibernate changes.
Anyway, you can start your investigation with:
var mockSession = new Mock<ISession>(MockBehavior.Strict); //this will make the mock to throw on each invocation which is not setup
var entities = mockSession.Object.Query<MyEntity>();
The second line above is going to throw an exception and show you which actual property/method on ISession the Query<T>() extension method tries to access, so you can set it accordingly. Keep going that way, and eventually you will have a good setup for your session so you can use it in the test.
Note: I'm not familiar with NHibernate, but I have used the above approach when I had to deal with extension methods from other libraries.
UPDATE for version 5:
In the new NHibernate versions Query<T> is part of the ISession interface, not an extension function, so it should be easy to mock.
Old answer:
I tried Sunny's suggestion and got this far but got stuck since the IQuery is cast to an NHibernate.Impl.ExpressionQueryImpl which is internal and I don't think can be extended. Just posting this to save other lost souls a couple hours.
var sessionImplMock = new Mock<NHibernate.Engine.ISessionImplementor>(MockBehavior.Strict);
var factoryMock = new Mock<NHibernate.Engine.ISessionFactoryImplementor>(MockBehavior.Strict);
var queryMock = new Mock<IQuery>(MockBehavior.Strict);//ExpressionQueryImpl
sessionImplMock.Setup(x => x.Factory).Returns(factoryMock.Object);
sessionImplMock.Setup(x => x.CreateQuery(It.IsAny<IQueryExpression>())).Returns(queryMock.Object);
sessionMock.Setup(x => x.GetSessionImplementation()).Returns(sessionImplMock.Object);

Sequence contains no elements when mocking repository

I'm trying to mock the repository code below:
var simulatorInstance = bundleRepository
.FindBy<CoreSimulatorInstance>(x => x.CoreSimulatorInstanceID == instanceID)
.Single();
but I get an error that states the "Sequence contains no elements". I tried changing the .Single to SingleOrDefault, but that returns a null.
In my unit test I mocked my the repository using the following:
This does not work
this.mockedRepository.Setup(
x => x.FindBy<CoreSimulatorInstance>(
z => z.CoreSimulatorInstanceID == 2))
.Returns(coreSimulatorInstancesList.AsQueryable());
This work for now using the Is.Any because I only have one record
this.mockedRepository.Setup(
x => x.FindBy<CoreSimulatorInstance>(
It.IsAny<Expression<Func<CoreSimulatorInstance, bool>>>()))
.Returns(coreSimulatorInstancesList.AsQueryable());
I want to mock the code using the .Single.
My guess is that you're using Moq to setup the repository's FindBy method so that business logic code hits the mocked method when you're running your test.
I think that the issue is that Moq is unable to match the parameter you provided during the mock setup with the parameter you provide during the execution of your business logic code, resulting in your mocked repository method not being executed and coreSimulatorInstancesList not being returned.
Since the parameter you provided to Moq during setup is an expression
z => z.CoreSimulatorInstanceID == 2
even when your business logic code executes with instanceID of 2
x => x.CoreSimulatorInstanceID == instanceID
resulting in an expression that is equivalent to the one you setup in the mock, they are still not matched because they are different expressions. These are two different expression objects. I don't think there's a way for Moq to know that they are equivalent and match your mocked method based on that.
I would go with Is.Any approach. If you want to be more thorough I think you would need to hand build a custom fake repository class in this case.

Categories