Matt M
Matt M

Reputation: 3779

When to specify certain Setups in Moq

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

Answers (1)

StuartLC
StuartLC

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 DbSetmock 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

Related Questions