I'm fairly new to testing with moq and I'm having a strange issue (at least it seems strange to me), but I'm probably just not setting up the mock object correctly. I have a repository layer that uses EntityFrameworkCore to work with my DbContext. One particular function in the repository allows me to return a sorted list without exposing the Linq or EFCore functions to the service layer calling the function.
Say I have a Model class like this:
public class SomeClass {
public string Foo { get; set; }
}
and I have a DbSet in my DbContext called someClasses. The three functions in my repository that I'm using to sort someClasses are these:
public async Task<List<SomeClass>> GetSomeClassesAsync(string orderBy = "", bool descending = false) {
var returnVals = _context.someClasses.AsQueryable();
returnVals = SortQueryableCollectionByProperty(returnVals, orderBy, descending);
return await returnVals.ToListAsync();
}
private IQueryable<T> SortQueryableCollectionByProperty<T>(IQueryable<T> queryable, string propertyName, bool descending) where T : class {
if (typeof(T).GetProperty(propertyName) != null) {
if (descending) {
queryable = queryable.OrderByDescending(q => GetPropertyValue(propertyName, q));
} else {
queryable = queryable.OrderBy(q => GetPropertyValue(propertyName, q));
}
}
return queryable;
}
private object GetPropertyValue<T>(string propertyName, T obj) {
return obj.GetType().GetProperty(propertyName).GetAccessors()[0].Invoke(obj, null);
}
So I have 2 unit tests for GetSomeClassesAsync(). The first unit test makes sure that the list returned is ordered by Foo, the second checks that an unordered list is returned when attempting to sort by Bar (a non-existent property). Here's how my tests are setup:
private Mock<DbContext> mockContext;
private MyRepository repo;
[TestInitialize]
public void InitializeTestData() {
mockContext = new Mock<DbContext>();
repo = new MyRepository(mockContext.Object);
}
[TestMethod]
public async Task GetSomeClassesAsync_returns_ordered_list() {
var data = new List<SomeClass> {
new SomeClass { Foo = "ZZZ" },
new SomeClass { Foo = "AAA" },
new SomeClass { Foo = "CCC" }
};
var mockSomeClassDbSet = DbSetMocking.CreateMockSet(new TestAsyncEnumerable<SomeClass>(data));
mockContext.Setup(m => m.someClasses).Returns(mockSomeClassDbSet.Object);
var sortedResults = await repo.GetSomeClassesAsync(nameof(SomeClass.Foo));
Assert.AreEqual("AAA", sortedResults[0].Foo);
Assert.AreEqual("CCC", sortedResults[1].Foo);
Assert.AreEqual("ZZZ", sortedResults[2].Foo);
}
[TestMethod]
public async Task GetSomeClassesAsync_returns_unordered_list() {
var data = new List<SomeClass> {
new SomeClass { Foo = "ZZZ" },
new SomeClass { Foo = "AAA" },
new SomeClass { Foo = "CCC" }
};
var mockSomeClassDbSet = DbSetMocking.CreateMockSet(new TestAsyncEnumerable<SomeClass>(data));
mockContext.Setup(m => m.someClasses).Returns(mockSomeClassDbSet.Object);
var unsortedResults = await repo.GetSomeClassesAsync("Bar");
Assert.AreEqual("ZZZ", unsortedResults[0].Foo);
Assert.AreEqual("AAA", unsortedResults[1].Foo);
Assert.AreEqual("CCC", unsortedResults[2].Foo);
}
DbSetMocking.CreateMockSet() was taken from here and TestAsyncEnumerable was taken from here
What I'm stumped on is the first test that returns an ordered list passes. Everything works fine. The second test fails and I get this error message:
System.NotImplementedException: The method or operation is not implemented.
This exception gets thrown when the code gets to ToListAsync(). What I don't get is why no error occurs when it goes through the work of sorting and then calling ToListAsync(), but when the sorting gets skipped and ToListAsync() is called, that exception is thrown. Am I not setting up my mock objects correctly?