0

I am using the ProjectTo<T> method to get data from the DB.

names = _mapper.ProjectTo<ItemNameDto>(query).ToList();

In the unit tests I would like to mock this method to return a specific collection.

Based on the second answer to this question - I figured out I need to pass all parameters to the setup.

When I pass null to the second parameter - the setup is ok, but when I pass null to the third one - the setup does not return the collection I want.

What value should I pass to the Expression parameter in this particular case? I really wouldn't like to leave it with It.IsAny<> because it seems too broad to me. I would like to write a setup which reflects exactly my use case.

_mapperMock
    .Setup(c => c.ProjectTo(
        It.Is<IQueryable<Item>>(x => x.HaveTheSameElements(filteredItems)),
        It.IsAny<object>(),
        It.IsAny<Expression<Func<ItemNameDto, object>>[]>()))
    .Returns(filteredItemNameDtos.AsQueryable());

This is the method signature I would like to setup.

IQueryable<TDestination> ProjectTo<TDestination>(IQueryable source, object parameters = null, params Expression<Func<TDestination, object>>[] membersToExpand);
Peter Csala
  • 17,736
  • 16
  • 35
  • 75
MiBuena
  • 451
  • 3
  • 22
  • Why not use the specific expression(s) that you will be testing in your setup? – Johnathan Barclay Oct 13 '20 at 11:30
  • In the original method usage, I don't pass any expression: names = _mapper.ProjectTo(query).ToList(); But in the test I need to pass one because otherwise I get this error. https://stackoverflow.com/questions/12413583/an-expression-tree-may-not-contain-a-call-or-invocation-that-uses-optional-argum – MiBuena Oct 13 '20 at 11:52
  • @MiBuena Why do you want to mock the AutoMapper? Why don't you use the configured mapping? – Peter Csala Oct 14 '20 at 08:08
  • @Peter Csala Because I am writing a unit test. Unit tests should mock all external dependencies. – MiBuena Oct 14 '20 at 08:44
  • @MiBuena You are using mocks to avoid side-effects and avoid expensive calls. In case of AutoMapper I don't see either. So, what is the primary need that indicates that you need to mock it? – Peter Csala Oct 14 '20 at 09:09
  • @PeterCsala, as advised in the book "The art of unit testing" external dependencies should be isolated, so that you test your code. – MiBuena Oct 14 '20 at 09:13
  • @MiBuena Yes I know, but what do you want to verify with the mocked mapper? What assumption is verified with the mocked mapper? – Peter Csala Oct 14 '20 at 09:19
  • @PeterCsala, in my case I have a method which gets data from the repository, makes a number of filterings, constructs a Response with the data and returns it. So in my unit tests I check a lot of things outside of the mapper. - whether the filtering is correct, what data the mapper is called with, etc. – MiBuena Oct 14 '20 at 09:29
  • @MiBuena Yepp, that's the point. You want to make sure your flawless and faulty repository are both handled in the correct way. You want to make sure that the filter is being applied. You want to make sure that the result as expected (in shape and in quantity) By verifying the result you are implicitly testing the mapper call(s) as well. So, I still don't see the point why do you need to mock the mapper. If I'm too much, please let me know and I'll stop immediately. – Peter Csala Oct 14 '20 at 09:46
  • @PeterCsala, if I use the real Automapper, I will be doing Integration testing, not unit testing. Integration testing should be done by the QA and a developer's job is to do the unit testing. What you are describing is necessary, but is out of the scope of unit testing. For Unit testing - I test that the automapper is called once and that it is called with the right collection. By testing that the right collection is passed to it - this is how I test MY filterings. For UT you fake the external library you use and test everything around it. – MiBuena Oct 14 '20 at 09:59
  • 1
    @PeterCsala, no leave them. Other people may benefit from the discussion. P.S. It may be component testing - but still I prefer to keep the pure track of UT as described by the book. I guess we can agree to disagree in this case. – MiBuena Oct 14 '20 at 10:30

1 Answers1

1

To mock a call to ProjectTo where no membersToExpand are provided, you should use It.Is:

_mapperMock
    .Setup(c => c.ProjectTo(
        It.Is<IQueryable<Item>>(x => x.HaveTheSameElements(filteredItems)),
        It.IsAny<object>(),
        It.Is<Expression<Func<ItemNameDto, object>>[]>(x => x.Length == 0)))
    .Returns(filteredItemNameDtos.AsQueryable());

x => x.Length == 0 indicates an empty array, which the framework instantiates when no params have been passed.

Johnathan Barclay
  • 18,599
  • 1
  • 22
  • 35