Reputation: 3779
I'm trying to follow this Get Started example for testing with Moq. I'm able to duplicate the examples within my own testing project and can get my tests to pass (testing my service where my context is injected). However, what I don't understand is WHEN to use each of the following Setup calls:
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
Can someone explain in very basic terms as to when each of these should be used?
For example, It seems that if the method in my service that I'm testing uses an expression, I need to do the 2nd setup call above (I've done some trial and error by removing and re-inserting these calls). I've been to the Moq documentation as well as MSDN for Table-TEntity and I still don't see it. Perhaps because I don't have a strong grasp of the Linq namespace.
Upvotes: 3
Views: 271
Reputation: 107237
TL;DR - When using an Entity Framework DBContext
dependency, you will need perform these Setups on any DBSet
which you intend to mock, specifically to return fake data to any LINQ queries on the DBSet. All 4 setups should be done for each mocked DbSet
- this can be done generically in a helper method.
In more Detail:
In general, with Strict mode off, Setup is only required on methods that you actually want to Mock. In this case, if you haven't done a Setup
on a method which is invoked during your Unit Test, Moq will instead provide default behaviour for any method which hasn't been explicitly Setup
, which typically is to return the default(T)
of any expected return type, T. For classes, the default is null, which isn't really going to help any during testing of classes dependent on a Mocked EF DbContext
.
The specific example you have provided is the standard mocked setup for an Entity Framework DbSet
, which then allows you to provide fake data for this specific DbSet (DbSet<Blog>
), by providing an alternative IQueryable<Blog>
from a List<Blog>
collection (as opposed to the usual concrete RDBMS implementation).
A suggestion would be to move the DbSet
mock code into your standard unit test plumbing setup framework / toolkit, to create a helper method like:
public static Mock<IDbSet<T>> GetMockedDbSet<T>(IList<T> fakeData) where T : class, new()
{
var data = fakeData.AsQueryable();
var mockSet = new Mock<IDbSet<T>>();
mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
return mockSet;
}
Which you can then set up on your Mock DBContext
, as follows:
var mockContext = new Mock<IMyDbContext>();
var mockBlogDbSet = GetMockedDbSet<Blog>(new List<Blog>{... fake data here ...});
mockContext.Setup(c => c.Blogs).Returns(mockBlogDbSet.Object);
var sut = new SomeClassIWantToTest(mockContext.Object); // Inject dependency into Ctor
sut.DoSomething();...
Upvotes: 3