如何对嵌套的foreach循环进行单元测试?

本文关键字:单元测试 循环 foreach 嵌套 | 更新日期: 2023-09-27 18:01:20

我有一个函数,它迭代父对象列表,并在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;
}

我如何为这个函数写单元测试?

如何对嵌套的foreach循环进行单元测试?

您可以使用的一种方法是使用与私有方法相同的方法来测试内部循环。也就是说,你不(至少,不是直接)。

不测试循环本身,而是验证GetParentAndChildObjects返回的结果。

通过为_dbRepository指定一个测试double,您可以完全控制插入到parentObjects列表中的对象的详细信息。因此,您的测试只需验证返回的对象列表是否符合预期。

Mark Seemann很好地描述了这种方法:

在回答了关于何时使用[存根和何时使用mock]的问题之后,几次,我得出了这个基于命令查询分离(CQS)语言的简单规则:

  • 使用模拟命令

  • 使用存根查询

这很有意义,因为命令都是关于副作用的,而mock都是关于行为验证的:也就是说,副作用发生了。另一方面,存根的存在主要是为了"制造快乐的噪音",它们必须做到这一点的方法之一是从依赖项中返回数据,当需要返回数据时。

像这样:

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 ...

通常,您将为内部循环创建一个函数(无论如何,对于可读性和嵌套来说,这可能是一件好事)。然后,一旦该函数就位,您就可以为它创建单元测试,就像为任何其他方法创建单元测试一样。

如果,出于某种原因你不想使用"提取方法"重构(这可以通过Resharper或其他重构工具自动化),恐怕你运气不好,因为单元测试需要有一个测试的入口点,而你不能进入另一个方法的中间。

您可能会考虑的一件事,虽然它更容易维护,是为单元测试提供一个调试版本,以及使用#if内联循环的优化版本。如果您使用的是。net 4.5或更高版本,您可以简单地将内部循环作为一种方法提取出来,并使用该方法的积极内联,这样您就不会有高维护(潜在)损失或提取方法性能的开销。