user3639321 user3639321 - 1 month ago 21
C# Question

Mocking async DbContext functions with input parameter with C#/Moq Framework

I have made a Mock of DbContext and populated with test data. DbSet is a protected class so the result cannot be mocked, so I found an extension method.

public static class DbSetMocking
{

private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data)
where T : class
{
var queryableData = data.AsQueryable();
var mockSet = new Mock<DbSet<T>>();

mockSet.As<IQueryable<T>>().Setup(m => m.Provider)
.Returns(queryableData.Provider);
mockSet.As<IQueryable<T>>().Setup(m => m.Expression)
.Returns(queryableData.Expression);
mockSet.As<IQueryable<T>>().Setup(m => m.ElementType)
.Returns(queryableData.ElementType);
mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator())
.Returns(queryableData.GetEnumerator());

return mockSet;
}

public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, TEntity[] entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}

public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IQueryable<TEntity> entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities);
return setup.Returns(mockSet.Object);
}

public static IReturnsResult<TContext> ReturnsDbSet<TEntity, TContext>(this IReturns<TContext, DbSet<TEntity>> setup, IEnumerable<TEntity> entities)
where TEntity : class
where TContext : DbContext
{
var mockSet = CreateMockSet(entities.AsQueryable());
return setup.Returns(mockSet.Object);
}
}


To create a Mock of DbContext:

var db = new Mock<DbContext>();

//Populate
this.db.Setup(x => x.MyObjects).ReturnsDbSet(
new List<MyObject>
{
new MyObject{Id=1, Description="Test"},
}
);


Second, I am trying to extend the Mocks to include Find(id) and FindAsync(id) methods. Theese methods are being place in the DbSetMocking class. Find method works fine:

mockSet.Setup(m => m.Find(It.IsAny<object[]>()))
.Returns<object[]>(id => StaticMethodtoFindStuff<T>(queryableData, id));


However, I cannot get the FindAsync method to work. This is what I have tried so far:

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns(Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, 1)));


This one works, but then I dont have access to the parameter set by the function. Tried this one, compiling works fine, but it fails on execution with the error msg:

Object of type 'System.Object[]' cannot be converted to type 'System.Threading.Tasks.Task`1[System.Object[]]'.

mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
.Returns<Task<object[]>>(d =>
{
return Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
});


Anybody have a clue how I can implement this function?

Answer

Finally got it figured out. Turned out that I was missing an 'async' keyword there. Code is:

        mockSet.Setup(m => m.FindAsync(It.IsAny<object[]>()))
            .Returns<object[]>(async (d) =>
            {
                return await Task.FromResult(StaticMethodtoFindStuff<T>(queryableData, d));
            });