3

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

Community
  • 1
  • 1
grimurd
  • 2,750
  • 1
  • 24
  • 39
  • While well-written, you question required a significant amount of legwork on my end to boil down into a problem statement. Try to create a better [SSCCE](http://sscce.org) next time to get an answer sooner. – Patrick Quirk Jul 14 '14 at 12:49
  • I was trying to make it simple but also trying include information that might be relevant. Guess i overdid it. Thanks for taking the trouble to look into it. I'll try your solution later today. – grimurd Jul 14 '14 at 15:56
  • No worries, it got the job done. Good luck! – Patrick Quirk Jul 14 '14 at 16:02

1 Answers1

2

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

Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • The test runs but to do it i must set the expressions when they are defined(otherwise there is a compiler error "local variable might not be initialized before accessing"). So the Setup() on containerRepoMock and queryFluentQueryMock don't seem to run correctly. At least they aren't replacing the pre-set expressions. Any idea what might be missing? – grimurd Jul 14 '14 at 21:29
  • Off the top of my head, no, I don't see why they wouldn't be working. Unfortunately I won't be in a position to debug for a few weeks. If you figure it out please come back and comment! – Patrick Quirk Jul 15 '14 at 15:43