Reputation: 2850
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
Upvotes: 3
Views: 2088
Reputation: 23747
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
Upvotes: 2