I\'d like to give the background to this question. Skip if you like. For quite a while I\'ve paid close attention to the on going debates on stackoverflow and elsewhere rega
Here is a complete example using Moq. You can paste the entire example into your unit test class. Thanks to comments by @jbaum012 and @Skuli. I also recommend the excellent tutorial from Microsoft.
// An Address entity
public class Address
{
public int Id { get; set; }
public string Line1 { get; set; }
}
// A Person referencing Address
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Address Address { get; set; }
}
// A DbContext with persons and devices
// Note use of virtual (see the tutorial reference)
public class PersonContext : DbContext
{
public virtual DbSet<Person> Persons { get; set; }
public virtual DbSet<Address> Addresses { get; set; }
}
// A simple class to test
// The dbcontext is injected into the controller
public class PersonsController
{
private readonly PersonContext _personContext;
public PersonsController(PersonContext personContext)
{
_personContext = personContext;
}
public IEnumerable<Person> GetPersons()
{
return _personContext.Persons.Include("Address").ToList();
}
}
// Test the controller above
[TestMethod]
public void GetPersonsTest()
{
var address = new Address { Id = 1, Line1 = "123 Main St." };
var expectedPersons = new List<Person>
{
new Person { Id = 1, Address = address, Name = "John" },
new Person { Id = 2, Address = address, Name = "John Jr." },
};
var mockPersonSet = GetMockDbSet(expectedPersons.AsQueryable());
mockPersonSet.Setup(m => m.Include("Address")).Returns(mockPersonSet.Object);
var mockPersonContext = new Mock<PersonContext>();
mockPersonContext.Setup(o => o.Persons).Returns(mockPersonSet.Object);
// test the controller GetPersons() method, which leverages Include()
var controller = new PersonsController(mockPersonContext.Object);
var actualPersons = controller.GetPersons();
CollectionAssert.AreEqual(expectedPersons, actualPersons.ToList());
}
// a helper to make dbset queryable
private Mock<DbSet<T>> GetMockDbSet<T>(IQueryable<T> entities) where T : class
{
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(entities.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(entities.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(entities.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(entities.GetEnumerator());
return mockSet;
}
For anyone who stumbles upon this issue with interest on how to solve the .Include("Foo")
problem with NSubstitute and Entity Framework 6+, I was able to bypass my Include
calls in the following way:
var data = new List<Foo>()
{
/* Stub data */
}.AsQueryable();
var mockSet = Substitute.For<DbSet<Foo>, IQueryable<Foo>>();
((IQueryable<Post>)mockSet).Provider.Returns(data.Provider);
((IQueryable<Post>)mockSet).Expression.Returns(data.Expression);
((IQueryable<Post>)mockSet).ElementType.Returns(data.ElementType);
((IQueryable<Post>)mockSet).GetEnumerator().Returns(data.GetEnumerator());
// The following line bypasses the Include call.
mockSet.Include(Arg.Any<string>()).Returns(mockSet);
Playing with this and referencing the answers here Setup result for call to extension method it looks like Moq cannot mock static extension methods
I tried to add:
mockSet.Setup(t => t.FirstAsync()).Returns(Task.FromResult(data.First()));
mockSet.Setup(t => t.FirstAsync(It.IsAny<Expression<Func<T, bool>>>())).Returns(Task.FromResult(data.First()));
And Moq complains that:
System.NotSupportedException : Expression references a method that does not belong to the mocked object: t => t.FirstAsync()
So it seems there are three options:
I managed to mock Include in Moq with a generic approach. Albeit this doesn't cover all usages of Include(), only with string and Expression, but it suited my needs:
public Mock<DbSet<T>> SetupMockSetFor<T>(Expression<Func<DbContext, DbSet<T>>> selector) where T : class
{
var mock = new Mock<DbSet<T>>();
mock.ResetCalls();
this.EntitiesMock.Setup(m => m.Set<T>()).Returns(mock.Object);
this.EntitiesMock.Setup(selector).Returns(mock.Object);
mock.Setup(x => x.Include(It.IsAny<string>())).Returns(mock.Object);
try
{
mock.Setup(x => x.Include(It.IsAny<Expression<Func<T, object>>>()))
.Returns(mock.Object);
}
catch
{
// Include only applies to some objects, ignore where it doesn't work
}
return mock;
}
test usage:
var mockCourseSet = SetupMockSetFor(entities => entities.Courses);
In service method:
var foundCourses = dbContext.Courses.Include(c => c.CourseParticipants).Where(c => c.Id = courseId)
The example DbSet
provided by the EF team is just that: an example.
If you want to mock Include
(or FindAsync), you'll have to do it yourself.