【问题标题】:Loop inside a unit test在单元测试中循环
【发布时间】:2011-08-16 09:36:58
【问题描述】:

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

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

如果没有循环语句,我想不出另一种方法来测试内部 IEnumerable。如果这是一个好的做法,请告诉我。

【问题讨论】:

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


    【解决方案1】:

    没有技术原因您不能这样做。一个单元测试中可以有多个 Assert 语句。在循环中包含 Assert 语句只是在测试中包含多个 Assert 语句的简写方式。

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

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

    如果您的方法返回一个产品的 IEnumerable,并且每个产品都包含一个颜色的 IEnumerable,那么我认为以下测试很好:

    [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)它测试“单一事物”(所有产品都有一些颜色)。 2)简单易读。
    • 嗯,是的..这就是我所需要的。我问这个问题是因为有些人说我们在每个测试中只需要一个 Assert,所以我想从他们那里知道我们如何继续测试这个方法:)
    • 我在测试中很少有多个 Assert 语句。但有时我确实有多个断言,或者循环中有一个断言。当我这样做时,我不会为此失眠。如果测试符合其目的并且写得很好 - 那么我继续下一个任务:-)
    • 好油漆。你可以做几个断言语句,但只能做一件事。最好的例子是断言返回对象的所有属性都是正确的。但是如果我使用多个断言,我通常会将它们提取到一个私有方法中,并注意给它一个有意义的名称,例如 AssertThatReturmedAdressObjectHasCorrectProperties。
    【解决方案2】:

    避免在测试中编写循环的一个原因是保持测试简洁且一目了然。由于您已使用 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” 但是是:

    但如果将“bit”更改为“bat”则通过。

    本书 xUnit 测试模式:重构测试代码 杰拉德·梅萨罗斯(Gerard Meszaros)

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

    【讨论】:

    • +1 建议使用 NUnit 约束,但我建议您可能希望将长度和“包含 a”约束拆分为单独的测试,或者至少单独的断言。
    【解决方案3】:

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

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

    1. 如上所述,测试非空迭代集。遍历空集是误报。假阳性结果是所有自动化测试的祸根,因为没有人会仔细检查绿色结果。
    2. 包括描述当前迭代的测试描述。至少包括迭代索引。

    这是我测试对象的大于属性的示例。

    [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 件事而不是一个。计数器大于等于大于,并且进行该评估的所有方法都应该是一致的。

    【讨论】:

      猜你喜欢
      • 2011-01-08
      • 1970-01-01
      • 2015-01-25
      • 1970-01-01
      • 1970-01-01
      • 2014-03-03
      • 2012-09-11
      • 2013-02-19
      • 2022-09-29
      相关资源
      最近更新 更多