2

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?

Community
  • 1
  • 1

1 Answers1

0

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);
});
  • I'm ultimately just trying to test the AllIncluding method. I setup the injection of the `Expression>` as an attempt to solve the issue that `query.Include(includeProperty)` always returns null. My guess is because query is of type `Mock` not IQueryable. How would the static class implementation be different than injecting the function? Also I've commented out the attempt to Mock the include method, I've updated my code to reflect my current code state. – Robert Garland Dec 02 '15 at 13:44
  • I did not realise that the code is using Include extension method from Entity framework library. – Edwin Sukirno Dec 02 '15 at 22:39
  • Generally I would avoid using DbSet directly and use Repository pattern instead. Perhaps you can create a fake DbSet as per this article http://romiller.com/2012/02/14/testing-with-a-fake-dbcontext/ – Edwin Sukirno Dec 02 '15 at 22:45
  • Thanks for the direction. I ended up creating a fake DbSet as per the article. – Robert Garland Dec 03 '15 at 21:11