我有一个函数可以迭代父对象列表,并为for-each循环中的每个父对象提取子对象。要获取子对象列表,该函数将调用另一个函数。这是功能:
public IEnumerable<IParentObject> GetParentAndChildObjects()
{
var parentObjects = new List<ParentObject>();
foreach (var item in _dbRepository.GetParentObjects())
{
var parentObject = new ParentObject() { Id = item.ID, Name = item.Name };
foreach (var subItem in _dbRepository.GetChildObjects(item.ID))
{
parentObjects.Children.Add(new ChildObject() { Id = subItem.ID, Name = subItem.Name });
}
}
return parentObjects;
}
如何为此功能编写单元测试?我正在使用Moq进行嘲弄。
答案 0 :(得分:3)
您可以使用的一种方法是以与私有方法相同的方式测试内部循环。也就是说,你没有(至少不是直接)。
不是测试循环本身,而是验证GetParentAndChildObjects
返回的结果。
通过为_dbRepository
指定测试双精度,您可以完全控制插入parentObjects
列表的对象的详细信息。因此,您的测试只能验证返回的对象列表是否符合预期。
Mark Seemann describes this approach well:
在回答了关于何时使用[存根和什么时候使用模拟]的问题之后,我根据命令查询分离(CQS)的语言得出了这个简单的规则:
使用模拟命令
使用存根进行查询
这很有意义,因为命令都是关于副作用的,而Mocks都是关于行为验证的:即发生了副作用。另一方面,存根主要存在于“发出快乐的声音”,并且它们必须这样做的一种方法是在需要返回数据时从依赖关系返回数据。
答案 1 :(得分:0)
通常,您将为内循环创建一个函数(无论如何,为了可读性和嵌套,这可能是一件好事)。然后,一旦该功能到位,您就可以像对待任何其他方法一样为其创建单元测试。
如果由于某种原因你不想使用"Extract Method" refactoring(可以使用Resharper或其他重构工具自动化),我担心你运气不好,因为单元测试需要有要测试的入口点,您不能在另一个方法的中间输入。
您可能会考虑的一件事,即更多维护,是使用#if
为单元测试提供一个调试版本,并使用内联循环优化版本。如果您使用的是.NET 4.5或更高版本,则可以简单地将内循环作为方法提取并使用aggressive inlining of the method,并且您不会有高维护(潜在)损失或性能的开销。提取方法。
答案 2 :(得分:0)
这样的事情:
var repo = new Mock<MyRepository>();
repo.Setup(r => r.GetParentObjects()).Returns(... a list with a couple of parent objects ...);
repo.SetUp(r => r.GetChildObjects(... id of first parent ...)).Returns(... list of child objects ...);
repo.SetUp(r => r.GetChildObjects(... id of secondparent ...)).Returns(... different list of child objects ...);
var result = new MyClass(repo.Object).GetParentAndChildObjects();
... assertions to check 2 parents with appropriate children returned ...