C# 如何使用Moq设置模拟上下文?
我已经使用洋葱架构和实体框架实现了一个用于数据访问的存储库模式,现在我想使用Moq对其进行测试。我刚才问了一个问题,我现在更困惑了,答案是好的,但我对如何嘲笑的理解很差,即使在读了doc之后。我想做的是测试存储库方法Getlong id。我的存储库构造函数将DbContext作为名为PrincipalServerContext的参数,因此建议我模拟该上下文以测试我的存储库。假设这是我的存储库: 最后,我的存储库方法知道存储库是泛型的,我使用POCO作为泛型类型:C# 如何使用Moq设置模拟上下文?,c#,.net,entity-framework,moq,repository-pattern,C#,.net,Entity Framework,Moq,Repository Pattern,我已经使用洋葱架构和实体框架实现了一个用于数据访问的存储库模式,现在我想使用Moq对其进行测试。我刚才问了一个问题,我现在更困惑了,答案是好的,但我对如何嘲笑的理解很差,即使在读了doc之后。我想做的是测试存储库方法Getlong id。我的存储库构造函数将DbContext作为名为PrincipalServerContext的参数,因此建议我模拟该上下文以测试我的存储库。假设这是我的存储库: 最后,我的存储库方法知道存储库是泛型的,我使用POCO作为泛型类型: public T Get(lon
public T Get(long id)
{
ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
ObjectSet<T> set = objContext.CreateObjectSet<T>();
IEnumerable<string> keyNames = set.EntitySet.ElementType
.KeyMembers
.Select(k => k.Name);
if (keyNames.Count() > 1)
return null;
else
{
string idName = keyNames.ElementAt(0); // For Document would be IDDocument
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, idName);
var idValue = Expression.Constant(id, id.GetType());
var equal = Expression.Equal(property, idValue);
var predicate = Expression.Lambda<Func<T, bool>>(equal, parameter);
return entities.SingleOrDefault(predicate);
//Returns the corresponding entity to 'id' and 'T'
}
}
这将使用适当的ID名称构建一个表达式,因为每个表都有不同的ID名称公司的策略
从这里告诉我的,我理解我必须为Mock对象构建一个返回类型,但是我的上下文类太薄了,我没有任何方法或任何东西,只有一个DbSet。
所以我试着把它作为一个测试,但它可能没有意义,因为它失败了,我真的很迷茫,不明白它:
Mock<PrincipalServerContext> moqContext;
public void IdExists(){
moqContext = new Mock<PrincipalServerContext>();
var set = new Mock<DbSet<Web_Documents>>();
moqContext.Setup(c => c.Set<Web_Documents>()).Returns(set.Object);
repoDoc = new Repository<Web_Documents>(moqContext.Object);
var testDoc = repoDoc.Get(1L);
Assert.AreEqual(testDoc.NomDocument, "Ajouter une catégorie");
}
假设我想做一个简单的测试,以确定搜索的ID是否对应于我的DB条目,我应该如何设置我试图定义的moqContext对象?在示例中,我看到他们通常有用于模拟对象的方法,但这里没有,所以我发现了这个方法,这让我尝试了这个测试
谢谢你的帮助 下面是一个使用内存数据库的示例 首先,创建一个请求,作为模拟工作单元的实例
[TestMethod]
public async Task ExampleTest() {
//arrange
Mock<IUnitOfWork> mockUow = MockUowFactory.Get(nameof(ExampleTest));
//act
using (var app = YOURAPP(mockUow.Object)){
app.METHODUNDERTEST();
}
//assert
...
}
然后构建模拟的工作单元。根据我阅读的内容,需要单独的上下文,一个用于播种,一个用于测试。MockEntityFactory只返回一个虚拟数据数组,用于填充InMemoryDatabase中的数据库集
public class MockUowFactory {
public static Mock<IUnitOfWork> Get(string dbName) {
DbContextOptions<YOUR CONTEXT> options = new DbContextOptionsBuilder<YOUR CONTEXT>()
.UseInMemoryDatabase(databaseName: dbName)
.Options;
using (var seedContext = new YOURCONTEXT(options)) {
seedContext.YOURENTITY.AddRange(MockEntityFactory.YOURENTITY);
seedContext.SaveChanges();
}
var context = new YOURCONTEXT(options);
var mockUow = new Mock<IUnitOfWork>();
mockUow.Setup(m => m.Context).Returns(context);
mockUow.Setup(m => m.Save()).Returns(() => context.SaveChanges().ToString());
return mockUow;
}
}
然后,我将此工作单元传递到必要的层,不需要做任何特殊的工作来测试我的生产代码。考虑保持存储库精简,并在该级别进行模拟。如果存储库没有业务逻辑,那么它可以作为单元测试的一个很好的分界点。这是我使用存储库模式的理由,因此我不必尝试模拟上下文。使用集成端到端测试来断言对于已知的数据状态,您的代码按预期运行。这样,单元测试可以在TDD中快速运行,而集成测试仍然覆盖您的端到端。@StevePy那么,您是说我不需要模拟上下文来测试这些方法,只需要进行经典的单元测试吗?对于集成测试,我将在使用存储库模式的MVC项目中测试控制器,但目前我仍然希望确保所有功能都正常。如果存储库很薄,它们只是根据简单的条件返回实体,或者保存/删除这些实体,那么它们就不会从单元测试中获得太多好处。例如,一个Get-by-ID,没有逻辑,它是到ORM的传递。您可能希望在存储库上测试的是存储库是否添加了任何较低级别的标准,如授权检查等。这些是在集成运行期间可以涵盖的内容。真正的逻辑是在控制器和服务中,模拟存储库使这些类更容易测试,您将在这里经常测试它们。我发现EF核心团队最近提供了在模拟上下文时利用内存中数据库的指导,这一点我做得很成功。使用此方法,您可以创建一个使用内存中数据库的模拟上下文,然后可以将数据播种到上下文中的不同DbSet中。然后,您可以传递该上下文,并根据该上下文执行查询。如果你感兴趣,我会花时间举个例子。@MORCHARD Yes肯定会有兴趣举个例子!!谢谢
[TestMethod]
public async Task ExampleTest() {
//arrange
Mock<IUnitOfWork> mockUow = MockUowFactory.Get(nameof(ExampleTest));
//act
using (var app = YOURAPP(mockUow.Object)){
app.METHODUNDERTEST();
}
//assert
...
}
public class MockUowFactory {
public static Mock<IUnitOfWork> Get(string dbName) {
DbContextOptions<YOUR CONTEXT> options = new DbContextOptionsBuilder<YOUR CONTEXT>()
.UseInMemoryDatabase(databaseName: dbName)
.Options;
using (var seedContext = new YOURCONTEXT(options)) {
seedContext.YOURENTITY.AddRange(MockEntityFactory.YOURENTITY);
seedContext.SaveChanges();
}
var context = new YOURCONTEXT(options);
var mockUow = new Mock<IUnitOfWork>();
mockUow.Setup(m => m.Context).Returns(context);
mockUow.Setup(m => m.Save()).Returns(() => context.SaveChanges().ToString());
return mockUow;
}
}