使用Moq验证List<>类型的参数
本文关键字:类型 参数 Moq 验证 List 使用 | 更新日期: 2023-09-27 18:13:06
使用Moq,我希望能够验证传递给模拟方法调用的参数是否满足某些条件。在这个场景中,我想检查传递给mock方法的列表是否具有一定的大小:
var mockSomeRepository = new Mock<SomeRepository>();
mockSomeRepository.Setup(m => m.Write(It.IsAny<List<SomeDTO>>())).Verifiable();
var mainClass = new MainClass(mockSomeRepository.Object);
List<SomeDTO> someList = GetListWith25Items();
mainClass.DoRepositoryWrite(someList); // calls SomeRepository.Write(someList);
mockSomeRepository.Verify(m =>
m.Write(It.Is<List<SomeDTO>>(l => l.Count() == 25)), Times.Once());
verify断言抛出一个异常,表示从未以这种方式调用该方法。然而,去除约束并使用Is.Any<List<SomeDTO>>()
会导致通过。我不确定我是否在这里正确地使用它。is<>() -这是我直观地想要我的测试看起来像,但我不确定我是否正确地使用了框架。我应该如何正确地设计这个测试?
您可以在模拟中去掉对Setup和Verifiable的调用。只需使用Verify。
我创建了一个小的测试项目,它为我工作:
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
namespace csharp
{
public class SomeDTO { }
public class SomeRepository { public virtual void Write(List<SomeDTO> list) { } }
public class MainClass
{
private SomeRepository someRepository;
public MainClass(SomeRepository someRepository) { this.someRepository = someRepository; }
public void DoRepositoryWrite(List<SomeDTO> list) { this.someRepository.Write(list); }
}
class Program
{
static void Main(string[] args)
{
var mockSomeRepository = new Mock<SomeRepository>();
var someList = Enumerable.Repeat(new SomeDTO(), 25).ToList();
var mainClass = new MainClass(mockSomeRepository.Object);
mainClass.DoRepositoryWrite(someList);
mockSomeRepository.Verify(m => m.Write(It.IsAny<List<SomeDTO>>()), Times.Once(), "Write was not called");
mockSomeRepository.Verify(m => m.Write(It.Is<List<SomeDTO>>(l => l.Count == 25)), Times.Once(), "Write was not called with a 25-element-list");
}
}
}
当我发布这个问题时,我错过了一些重要的细节。我想详细说明一下实际发生了什么,以防将来对某人有所帮助。我所测试的方法实际上是清除传递给mock的列表:
public class SomeDTO { }
public class SomeRepository
{
public virtual void Write(IEnumerable<SomeDTO> list) { }
}
public class MainClass
{
private readonly SomeRepository _someRepository;
private readonly List<SomeDTO> _testList = new List<SomeDTO>();
public MainClass(SomeRepository someRepository)
{
_someRepository = someRepository;
}
public void DoRepositoryWrite()
{
_testList.AddRange(Enumerable.Repeat(new SomeDTO(), 25));
_someRepository.Write(_testList);
_testList.Clear();
}
}
class Program
{
static void Main(string[] args)
{
var mockSomeRepository = new Mock<SomeRepository>();
var mainClass = new MainClass(mockSomeRepository.Object);
mainClass.DoRepositoryWrite();
mockSomeRepository.Verify(m => m.Write(It.IsAny<IEnumerable<SomeDTO>>()), Times.Once(), "Write was not called");
mockSomeRepository.Verify(m => m.Write(It.Is<IEnumerable<SomeDTO>>(l => l.Count() == 25)), Times.Once(), "Write was not called with a 25-element-list");
}
}
回头看似乎有点明显,但对我来说,这里的要点是mock挂在对传递给它的列表的引用上。因此,您需要小心任何改变该列表的副作用。
为了正确地编写测试,我需要在调用mock方法时立即检查传递的列表的属性。为此,我使用了一个回调:
int listCountAtTimeOfCall = 0;
mockSomeRepository.Setup(
m => m.Write(It.IsAny<IEnumerable<SomeDTO>>())).Callback
<IEnumerable<SomeDTO>>(list => listCountAtTimeOfCall = list.Count());
... do the work ...
Assert.AreEqual(listCountAtTimeOfCall, 25);