Search code examples
c#linqunit-testingxunit.netfakeiteasy

How to use FakeItEasy to fake a complex linq query parsing to dictionary


I'm trying to create a testing method using fakeItEasy for the following peace of code. It queries a list of objects from the database and creates a distinct dictionary from it:

var animals = _dataService.Query<Animal>()
    .Where(x => x.Species == 2
    .GroupBy(i => i.Code)
    .Select(i => i.FirstOrDefault())
    .ToDictionary(i => i.Code, i => i);

I'm configuring the test this way:

// Arrange
var fakeIDataService = A.Fake<IDataService>();
var fakeMapper = A.Fake<IMapper>();

var monkey = new Animal
{
    Id = 1,
    Description = "Monkey",
    Code = "MKY",
    Species = 2
};

var animals = new Dictionary<string, Animal>() 
{ 
    { monkey.Code, monkey} 
};

A.CallTo(() => fakeIDataService.Query<Animal>()
    .Where(x => x.Species == 2
    .GroupBy(i => i.Code)
    .Select(i => i.FirstOrDefault())
    .ToDictionary(i => i.Code, i => i)
).Returns(animals);

// Act
var service = new ProcessAnimalLines(fakeIDataService, fakeMapper);

// Assert
A.CallTo(() => fakeIDataService.SaveChangesAsync()).MustHaveHappened(Repeated.Exactly.Once);

When debugging the compiler breakes trying to create the A.CallTo that returns the animals dictionary, it doesn't even get to the act part.

This is the error visual studio throws:

System.ArgumentException : The specified object is not recognized as a fake object.

It works by getting a list from the database and then transforming it manually into a dictionary, but I would really like to make it right all at once.


Solution

  • Making this an answer from our comments.

    The part you want to fake is the IQueryable<> as that leads to a dependency on an external resource (database), which we know is problematic for unit tests, not the ToDictionary().

    This removes the dependency, but still allows you to test the logic of the query in the context of the thing that calls your data service.

    As you said, something like this

    var animals= new List<Animal>() { monkey };
    A.CallTo(() => fakeIDataService
        .Query<Animal>())
        .Returns(animalList.AsQueryable())