使用 Moq 框架进行模拟时将 DbSet 与 T 匹配
我正在使用 Entity Framework 6 和 Moq 框架。目前我正在编写一些单元测试,在每个测试中我需要为每个测试设置一个具有适当类型的数据集。 其中一个单元测试如下所示:
[TestMethod]
public async Task GetAllCaseCategories_WithEmptyDataset_ReturnsEmpty()
{
var data = new List<DataAccessLayer.Tables.CaseCategory>(){};
var mockContext = GetMockContextWithCaseCategoryDataSetAsync(data);
var mockLogger = new Mock<IDatabaseContextLogging>();
DatabaseTablesAccess databaseAccess = new DatabaseTablesAccess(mockDatabaseContext.Object, mockLogger.Object);
List<OneCaseCategory> caseCategories = await databaseAccess.GetAllCaseCategories();
Assert.IsTrue(caseCategories.Count().Equals(0), "caseCategories should not contain any items.");
}
生成数据库上下文模拟对象的方法包含在名为“GetMockContextWithCaseCategoryDataSetAsync”的方法中。如下:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync(List<CaseCategory> data)
{
var mockCaseCategorySet = new Mock<DbSet<CaseCategory>>() { };
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<CaseCategory>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<CaseCategory>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<CaseCategory>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.CaseCategories).Returns(mockCaseCategorySet.Object);
return m;
}
我在数据访问对象中有多个表,并且为每个表编写了一种方法,该方法返回数据访问对象,并将适当类型的数据绑定到模拟对象中的正确表。我想概括这一点,这是我的尝试:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync<T>(List<T> data) where T : class
{
var mockCaseCategorySet = new Mock<DbSet<T>>() { };
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<T>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<T>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<T>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.?).Returns(mockCaseCategorySet.Object);
return m;
}
一切都很好,直到我设置哪个表返回模拟对象的方法的通用版本中的倒数第二行。我不知道如何(如果可能的话)根据传入的泛型控制使用哪个表。 我想这样使用它:
var context = GetMockContextWithCaseCategoryDataSetAsync<CaseCategory>(data);
I am using Entity Framework 6 and Moq framework. Currently I am writing a few Unit Tests where in each test I need to set up a dataset with the appropriate type for each test.
One of the unit tests looks like this:
[TestMethod]
public async Task GetAllCaseCategories_WithEmptyDataset_ReturnsEmpty()
{
var data = new List<DataAccessLayer.Tables.CaseCategory>(){};
var mockContext = GetMockContextWithCaseCategoryDataSetAsync(data);
var mockLogger = new Mock<IDatabaseContextLogging>();
DatabaseTablesAccess databaseAccess = new DatabaseTablesAccess(mockDatabaseContext.Object, mockLogger.Object);
List<OneCaseCategory> caseCategories = await databaseAccess.GetAllCaseCategories();
Assert.IsTrue(caseCategories.Count().Equals(0), "caseCategories should not contain any items.");
}
The method that generates the database context mock object is contain in the method called "GetMockContextWithCaseCategoryDataSetAsync". Here it is:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync(List<CaseCategory> data)
{
var mockCaseCategorySet = new Mock<DbSet<CaseCategory>>() { };
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<CaseCategory>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<CaseCategory>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<CaseCategory>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<CaseCategory>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.CaseCategories).Returns(mockCaseCategorySet.Object);
return m;
}
I have multiple tables in the data access object and I have written a method for each one that returns the data access object with the appropriate type of data bound to the correct table in the mock object. I would like to generalize this and here is my attempt:
public Mock<Context> GetMockContextWithCaseCategoryDataSetAsync<T>(List<T> data) where T : class
{
var mockCaseCategorySet = new Mock<DbSet<T>>() { };
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<T>(data.AsQueryable().Provider));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.Expression).Returns(data.AsQueryable().Expression);
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(data.AsQueryable().ElementType);
mockCaseCategorySet.As<IDbAsyncEnumerable<T>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<T>(data.AsQueryable().GetEnumerator()));
mockCaseCategorySet.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(data.AsQueryable().GetEnumerator());
Mock<Context> m = new Mock<Context>();
m.Setup(x => x.?).Returns(mockCaseCategorySet.Object);
return m;
}
Everything is good up until that second last line in the generic version of the method where I am setting up which table returns the mocked object. I am lost as to how (if even possible) I control which table is used based on the generic passed in.
I would like to use it like this:
var context = GetMockContextWithCaseCategoryDataSetAsync<CaseCategory>(data);
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我看不到一种方法可以完成您想要做的事情,除非您还通过通用
Set
方法(如_dbContext .Set().Select(x => x.Id)
)。我一直在使用的另一种通用设置是创建一个“MockedDbContext”类,我在访问数据库的所有测试中重用该类。这通常看起来像这样;
我这样使用它;
它内部使用一个
InMemoryDbSet
,如下所示;这在编写单元测试时节省了大量工作,并且是一种一次性设置,这正是我认为您正在寻找的。
I don't see a way to do what you're trying to do unless you also access the
DbSets
by the genericSet<TEntity>
method (like_dbContext.Set<Salary>().Select(x => x.Id)
).An alternative setup that I have been using and that is sort of generic is to create a "MockedDbContext" class that I reuse in all tests accessing the database. This usually looks something like this;
I use it like this;
And It internally uses an
InMemoryDbSet<T>
that looks like this;This saves a lot of work when writing unit tests and is kind of a one time setup which is what I think you were looking for.