如何使用 Moq 模拟 DbSet<T>.Load()?
How to mock DbSet<T>.Load() using Moq?
我正在尝试对以下代码行进行单元测试:
myDbContext.myDbSet.Load()
myObservableCollection = myDbContext.myDbSet.Local
我的测试方法是这样的:
Dim dataMyDbSet = New List(Of myDbSet) From {
new myDbSet() With{.Id=1},
new myDbSet() With{.Id=1},
new myDbSet() With{.Id=1}
}.AsQueryable()
Dim myDbSetMock = New Mock(Of DbSet(of myDbSet))
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.Provider).Returns(dataMyDbSet.Provider)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.Expression).Returns(dataMyDbSet.Expression)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.ElementType).Returns(dataMyDbSet.ElementType)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.GetEnumerator).Returns(dataMyDbSet.GetEnumerator)
myDbSetMock.Setup(Function(x) x.Local).Returns(New ObservableCollection(Of myDbSet)(dataMyDbSet))
Dim MyDbContextMock = New Mock(of dbCrashtestNcap)
MyDbContextMock.Setup(function (f) f.myDbSet).Returns(myDbSetMock.Object)
Dim crashService = New CrashService(MyDbContextMock.Object)
Dim result = crashService.GetOcNcapKlassen()
Assert.That(result.Count,[Is].EqualTo(3))
这让我在第一行出现 NullReferenceException myDbContext.myDbSet.Load()
我所有尝试模拟 Load() 或 return myDbContext.myDbSet 的值(以便 NullPointerException
不存在)都失败了
一个可行的(肮脏的)解决方法是更改 ToList() 中的 Load() 但我更愿意保留 Load() 或至少了解是否有解决方案。在此先感谢您的帮助。解决方案可以是 C# 或 vb.net 这对我来说并不重要
您已经成功地为 Local
设置了一个模拟,它是 DbSet<T>
类型的 属性,您也模拟了它。 Load
方法是一种静态方法(我相信它甚至是一种扩展方法,但这对这种情况并不重要)。像 Moq 这样的框架使用代理技术来为不同类型的对象设置模拟。虽然这是一个很好的解决方案,但它禁止您模拟 virtual
或 abstract
类型以外的任何类型,因此您不能使用 Moq 开箱即用地模拟静态 Load
方法。
您可以在单元内创建一个新方法,将其标记为 virtual
,然后在其中调用 Load
方法:
public class Unit
{
public void YourMethod()
{
// .. do stuff
CallStaticMethod(/* maybe pass parameters? */);
// .. do more stuff
}
public virtual void CallStaticMethod()
{
// call static method
Console.WriteLine("This is a static method call!");
}
}
public class UnitUnderTest : Unit
{
public override void CallStaticMethod()
{
// do whatever you want during testing only
// possibly call base.CallStaticMethod() to execute base method.
}
}
现在,在您的单元测试中,您需要使用 UnitUnderTest
并修改 class 以使其符合您的需要。
正如 Ivan Stoev 在评论中提到的(再次感谢!)我的问题是 Load()
不在 IQueryable 的通用版本上运行,而是在非通用版本上运行。因此,为了解决这个问题,我将上面的四行替换为以下几行并且有效:
myDbSetMock.As(of IQueryable).Setup(Function(m) m.Provider).Returns(dataMyDbSet.Provider)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.Expression).Returns(dataMyDbSet.Expression)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.ElementType).Returns(dataMyDbSet.ElementType)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.GetEnumerator).Returns(dataMyDbSet.GetEnumerator)
但是,如果您想使用 ToList(),则需要通用版本。
我正在尝试对以下代码行进行单元测试:
myDbContext.myDbSet.Load()
myObservableCollection = myDbContext.myDbSet.Local
我的测试方法是这样的:
Dim dataMyDbSet = New List(Of myDbSet) From {
new myDbSet() With{.Id=1},
new myDbSet() With{.Id=1},
new myDbSet() With{.Id=1}
}.AsQueryable()
Dim myDbSetMock = New Mock(Of DbSet(of myDbSet))
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.Provider).Returns(dataMyDbSet.Provider)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.Expression).Returns(dataMyDbSet.Expression)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.ElementType).Returns(dataMyDbSet.ElementType)
myDbSetMock.As(of IQueryable (Of myDbSet)).Setup(Function(m) m.GetEnumerator).Returns(dataMyDbSet.GetEnumerator)
myDbSetMock.Setup(Function(x) x.Local).Returns(New ObservableCollection(Of myDbSet)(dataMyDbSet))
Dim MyDbContextMock = New Mock(of dbCrashtestNcap)
MyDbContextMock.Setup(function (f) f.myDbSet).Returns(myDbSetMock.Object)
Dim crashService = New CrashService(MyDbContextMock.Object)
Dim result = crashService.GetOcNcapKlassen()
Assert.That(result.Count,[Is].EqualTo(3))
这让我在第一行出现 NullReferenceException myDbContext.myDbSet.Load()
我所有尝试模拟 Load() 或 return myDbContext.myDbSet 的值(以便 NullPointerException
不存在)都失败了
一个可行的(肮脏的)解决方法是更改 ToList() 中的 Load() 但我更愿意保留 Load() 或至少了解是否有解决方案。在此先感谢您的帮助。解决方案可以是 C# 或 vb.net 这对我来说并不重要
您已经成功地为 Local
设置了一个模拟,它是 DbSet<T>
类型的 属性,您也模拟了它。 Load
方法是一种静态方法(我相信它甚至是一种扩展方法,但这对这种情况并不重要)。像 Moq 这样的框架使用代理技术来为不同类型的对象设置模拟。虽然这是一个很好的解决方案,但它禁止您模拟 virtual
或 abstract
类型以外的任何类型,因此您不能使用 Moq 开箱即用地模拟静态 Load
方法。
您可以在单元内创建一个新方法,将其标记为 virtual
,然后在其中调用 Load
方法:
public class Unit
{
public void YourMethod()
{
// .. do stuff
CallStaticMethod(/* maybe pass parameters? */);
// .. do more stuff
}
public virtual void CallStaticMethod()
{
// call static method
Console.WriteLine("This is a static method call!");
}
}
public class UnitUnderTest : Unit
{
public override void CallStaticMethod()
{
// do whatever you want during testing only
// possibly call base.CallStaticMethod() to execute base method.
}
}
现在,在您的单元测试中,您需要使用 UnitUnderTest
并修改 class 以使其符合您的需要。
正如 Ivan Stoev 在评论中提到的(再次感谢!)我的问题是 Load()
不在 IQueryable 的通用版本上运行,而是在非通用版本上运行。因此,为了解决这个问题,我将上面的四行替换为以下几行并且有效:
myDbSetMock.As(of IQueryable).Setup(Function(m) m.Provider).Returns(dataMyDbSet.Provider)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.Expression).Returns(dataMyDbSet.Expression)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.ElementType).Returns(dataMyDbSet.ElementType)
myDbSetMock.As(of IQueryable).Setup(Function(m) m.GetEnumerator).Returns(dataMyDbSet.GetEnumerator)
但是,如果您想使用 ToList(),则需要通用版本。