在单元测试中循环

时间:2011-05-01 09:44:56

标签: c# unit-testing nunit for-loop ienumerable

我们可以在单元测试中使用循环吗?

我的方法返回IEnumerable<IEnumerable>,我想在创建IEnumerable<IEnumerable>的地方对这个逻辑进行单元测试。基本上我想测试IEnumerable中元素的数量是否符合预期。

在没有循环语句的情况下,我无法找出测试内部IEnumerable的替代方法。如果这是一个很好的做法,请告诉我。

3 个答案:

答案 0 :(得分:21)

没有技术理由你不能这样做。您可以在单元测试中使用多个Assert语句。在循环中使用Assert语句只是在测试中使用多个Assert语句的简便方法。

但是,有些人认为在单元测试中应该只有一个Assert语句。

我个人不同意 - 我认为测试应该测试一件事 - 为了做到这一点,有时您可能需要多个Assert语句。

如果你的方法返回一个IEnumerable的产品,并且每个产品包含一个IEnumerable of Color,那么我认为以下测试是正常的:

[Test]
public void All_products_should_have_some_colors()
{
    var products = GetProducts();

    foreach (var product in products)
    {
        Assert.IsNotEmpty(product.Colors);
    }
}

但是,您需要注意,如果IEnumerable包含0个元素,则循环将永远不会执行任何Assert语句,并且您的单元测试将“通过” - 而您可能会打算失败。

为了解决这种情况,你可以进行单独的测试,确保IEnumerable中有超过0个元素(即GetProducts确实返回了一些产品):

Assert.IsNotEmpty(products);

答案 1 :(得分:8)

避免在测试中编写循环的一个原因是让测试简洁易读。由于您已使用NUnit标记了问题,并且您说您只想测试元素计数是否符合预期,请考虑使用NUnit Constraints制作断言。

例如,

IEnumerable<IEnumerable<char>> someStrings = new[] { "abc", "cat", "bit", "hat" };

Assert.That(someStrings, Has.All.With.Length.EqualTo(3).And.All.Contains("a"));

失败并显示以下消息:

预期:所有项目属性长度等于3,所有项目字符串包含“a”   但是:&lt; “abc”,“cat”,“bit”,“hat”&gt;

但如果你将“bit”改为“bat”,则会通过。

本书xUnit测试模式:重构测试代码 作者Gerard Meszaros

对你这样的问题有很多很好的答案。

答案 2 :(得分:2)

是的,您可以在单元测试中使用循环,但要小心。正如Alex York所提到的,如果你测试一个的东西,循环是可以接受的;即一个期望。

如果你使用循环,那么我建议你必须做两件事:

  1. 如上所述,测试非空迭代集。迭代空集是一种误报。误报结果是所有自动化测试的祸根,因为没有人双重检查绿色结果。
  2. 包含描述当前迭代的测试描述。至少包括迭代索引。
  3. 以下是我测试对象的大于属性的示例。

    [Test]
    public void TestCompare_XtoY_GreaterThan()
    {
      int numObjects = mOrderedList.Count;
      for (int i = 1; i < numObjects; ++i)
      {
        for (int j = 0; j < i; ++j)
        {
          string testDescription = string.Format("{0} is greater than {1} which implies\n  {2}\n    is greater than\n  {3}"
                                                , i, j
                                                , mOrderedList[i], mOrderedList[j]
                                                );
          Assert.IsTrue(0 < mOrderedList[i].CompareTo(mOrderedList[j]), testDescription);
          Assert.IsTrue(0 < mOrderedList[i].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
          Assert.IsTrue(0 < mOrderedList[j].Compare(mOrderedList[i], mOrderedList[j]), testDescription);
          Assert.Greater(mOrderedList[i], mOrderedList[j], testDescription);
        }
      }
    }
    

    我在测试设置中使用以下方法测试我的有序列表是否为空

    [SetUp]
    public void GeneralTestSetup()
    {
      // verify the iterated sources are not empty
      string testDescription = string.Format("The ordered list of objects must have at least 3 elements, but instead has only {0}.", mOrderedList.Count);
      Assert.IsTrue(2 < mOrderedList.Count, testDescription);
    }
    

    即使在我的循环中我也有多个断言,但所有断言都在测试单个期望:

    if i > j then mOrderedList[i] > mOrderedList[j]
    

    迭代级别的测试描述使您获得失败,例如:

    10 is greater than 9 which implies
      TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, VerifyReadOnly]
        is greater than
      TestActionMethodInfo: [Actions.File, Version=1.0.446.0, File, Write]
    Expected: True
    But was:  False
    

    而不只是:

    Expected: True
    But was:  False
    

    关于我的代码的问题/辩论:

    我正在测试一件事吗?

    我在对象中主张4种不同的比较方法,可以说是测试4个不是一个的东西。计数器大于大于大于,并且所有进行评估的方法应该是一致的。