2
votes

I'm new to Moq Framework and Unit Testing in general, I'm trying to create a unit test for my repository function below.

Repository Method

public IQueryable<Campaign> AllIncluding(params Expression<Func<Campaign, object>>[] includeProperties)
{
    IQueryable<Campaign> query = _context.Campaigns;
    foreach (var includeProperty in includeProperties)
    {
        query = query.Include(includeProperty);
    }
    return query;
}

Unit Test with Moq Setup

//Arrange
var data = new List<Models.Campaign>
{
    new Models.Campaign { Id = 1, Name = "Campaign Past", StartDate = DateTime.Now.AddDays(-10), EndDate = DateTime.Now.AddDays(-5) },
    new Models.Campaign { Id = 2, Name = "Campaign Active", StartDate = DateTime.Now.AddDays(-4), EndDate = DateTime.Now.AddDays(3) },
    new Models.Campaign { Id = 2, Name = "Campaign Future", StartDate = DateTime.Now.AddDays(4), EndDate = DateTime.Now.AddDays(10) }
}.AsQueryable();
var _moqSet = new Mock<DbSet<Models.Campaign>>();
_moqSet.As<IQueryable<Models.Campaign>>().Setup(m => m.Provider).Returns(data.Provider);
_moqSet.As<IQueryable<Models.Campaign>>().Setup(m => m.Expression).Returns(data.Expression);
_moqSet.As<IQueryable<Models.Campaign>>().Setup(m => m.ElementType).Returns(data.ElementType);
_moqSet.As<IQueryable<Models.Campaign>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
//_moqSet.As<IQueryable<Models.Campaign>>()
//    .Setup(m => m.Include(It.IsAny<Expression<Func<Models.Campaign, object>>>()))
//    .Returns((Expression<Func<Models.Campaign, object>> predicate) =>
//    {
//        return _moqSet.Object.Include(predicate);
//    });
var _moqContext = new Mock<Context.IPrizeSelectionContext>();
_moqContext.Setup(m => m.Campaigns).Returns(_moqSet.Object);
Func<IQueryable<Models.Campaign>, Expression<Func<Models.Campaign, object>>, IQueryable<Models.Campaign>> includeMethod = (query, expression) =>
{
    return query.Include(expression);
};
//Act
List<Models.Campaign> allResults = null;
using (var sut = new CampaignRepository(_moqContext.Object, includeMethod))
{
    allResults = sut.AllIncluding(o => o.Id, o => o.Name).OrderBy(o => o.Id).ToList();
}
//Assert
Assert.IsNotNull(allResults);
Assert.AreEqual(3, allResults.Count);
Assert.IsNull(allResults[0].StartDate);

I'm getting a null data after the query.include(includeProperty) I've tried to mock the include function but I get an Expression references a method that does not belong to the mocked object: m => m.Include<Campaign,Object>(It.IsAny<Expression'1>()) exception when trying to setup the Mock DbSet. After some digging I've found that Moq has issues or can't mock extension methods, see Question Mocking Extionsion Methods with Moq

Following the steps in This Blog I've tried to execute the delegate pattern

Repository Re-write

Func<IQueryable<Campaign>, Expression<Func<Campaign, object>>, IQueryable<Campaign>> _includeMethod = null;
...
public CampaignRepository(IPrizeSelectionContext context, Func<IQueryable<Campaign>, Expression<Func<Campaign, object>>, IQueryable<Campaign>> includeMethod)
    : this(context)
{
    _includeMethod = includeMethod;
}
...
public IQueryable<Campaign> AllIncluding(params Expression<Func<Campaign, object>>[] includeProperties)
{
    IQueryable<Campaign> query = _context.Campaigns;
    foreach (var includeProperty in includeProperties)
    {
        if (_includeMethod == null)
            query = query.Include(includeProperty);
        else
            query = _includeMethod(query, includeProperty);
    }
    return query;
}

Here is my Unit Test setup

Func<IQueryable<Models.Campaign>, Expression<Func<Models.Campaign, object>>, IQueryable<Models.Campaign>> includeMethod = (query, expression) =>
{
    return query.Include(expression);
};

List<Models.Campaign> allResults = null;
using (var sut = new CampaignRepository(_moqContext.Object, includeMethod))
{
    allResults = sut.AllIncluding(o => o.Id, o => o.Name).OrderBy(o => o.Id).ToList();
}

However this pattern also ends the same way, the IQueryable<Campaign> query = _context.Campaigns; returns the Mock type but I cant use the extension method.

Can anyone point me in the correct direction to test my repository method?

1

1 Answers

0
votes

Looks like you are trying to test 2 things here: 1. Include extension method 2. AllIncluding method

I suggest you to separate the Include method into a different static class such as:

public static class CollectionExtensions {
public static Include(this IQueryable<Campaign>, Expression<Func<Campaign, object>> includeProperty) {
    // put implementation here
}}

This way, you can test Include method separately. (Most of the logic seems to be inside the include method anyway)

Also I don't think you can use _moqSet.Object inside _moqSet setup as shown below. This is probably what causing moq to not able to resolve the expression.

_moqSet.As<IQueryable<Models.Campaign>>()
.Setup(m => m.Include(It.IsAny<Expression<Func<Models.Campaign, object>>>()))
.Returns((Expression<Func<Models.Campaign, object>> predicate) =>
{
    return _moqSet.Object.Include(predicate);
});