在单元测试中循环

发布于 2024-11-04 01:43:38 字数 261 浏览 0 评论 0原文

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

我的方法返回一个 IEnumerable,我想在创建 IEnumerable 的位置对该逻辑进行单元测试。基本上我想测试 IEnumerable 中的元素计数是否符合预期。

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

Can we have a loop inside a unit test?

My method returns an IEnumerable<IEnumerable>, I would like to unit test this logic where the IEnumerable<IEnumerable> is created. Basically I wanna test if the count of elements in the IEnumerable are as expected.

I cannot figure out an alternate way to test the inner IEnumerable without having a looping statement. Please let me know if this is a good practice.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

仲春光 2024-11-11 01:43:38

没有任何技术原因你不能做到这一点。一个单元测试中可以有多个 Assert 语句。在循环中使用 Assert 语句只是在测试中使用多个 Assert 语句的简写方式。

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

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

如果您的方法返回 Product 的 IEnumerable,并且每个 Product 包含 Color 的 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 实际上返回一些 Product):

Assert.IsNotEmpty(products);

There is no technical reason you can't do it. You can have multiple Assert statements in a unit test. Having an Assert statement in a loop is simply a shorthand way of having multiple Assert statements in a test.

However, some people think there should only be a single Assert statement in a unit test.

I personally don't agree - I think a test should test a single thing - and in order to do that sometimes you may need more than one Assert statement.

If your method returns an IEnumerable of Product's, and each Product contains an IEnumerable of Color's, then I think the following test is is fine:

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

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

However, you do need to be aware that if the IEnumerable contains 0 elements, the loop will never execute any of the Assert statements and your unit test will "pass" - whereas you probably would have intended it to fail.

To remedy that situation, you could have a separate test making sure that there are more than 0 elements in the IEnumerable (i.e. that GetProducts does actually return some Product's):

Assert.IsNotEmpty(products);
过潦 2024-11-11 01:43:38

避免在测试中编写循环的一个原因是保持测试简洁且一目了然。由于您已使用 NUnit 标记了问题,并且您说您只想测试元素计数是否符合预期,因此请考虑使用 NUnit 约束

例如,

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

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

失败并显示消息:

预期:所有项目属性长度等于 3 且所有项目字符串包含“a”
但是是:< "abc", "cat", "bit", "hat" >

但如果将 "bit" 更改为 "bat",则会通过。

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

对于像您这样的问题有很多很好的答案。

One reason to avoid writing a loop in a test would be to keep the test concise and readable at a glance. Since you have tagged the question with NUnit and you say you just want to test that the element count is as expected, consider making your Asserts using the NUnit Constraints.

For example,

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

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

fails with the message:

Expected: all items property Length equal to 3 and all items String containing "a"
But was: < "abc", "cat", "bit", "hat" >

but passes if you change "bit" to "bat".

The book xUnit Test Patterns: Refactoring Test Code
By Gerard Meszaros

has many great answers to questions such as yours.

北凤男飞 2024-11-11 01:43:38

是的,单元测试中可以有循环,但要小心。正如 Alex York 所提到的,如果您测试一个事物,则循环是可以接受的;即一种期望。

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

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

Yes you can have loops in unit test, but with caution. As mentioned by Alex York, loops are acceptable if you test one thing; i.e. one expectation.

If you use loops, then I recommend that you must do two things:

  1. As mentioned above, test for a non-empty iteration set. Iterating over an empty set is a false positive. False positive results are bane of all automated testing because nobody double checks a green result.
  2. Include a test description that describes the current iteration. At a minimum include the iteration index.

Here is an example from my testing of the Greater Than property of an object.

[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);
    }
  }
}

I test that my ordered list is non-empty in the test setup using:

[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);
}

I have multiple Asserts even within my loop, but all of the asserts are testing the single expectation:

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

The test description at the iteration level is so you get Failures such as:

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

instead of just:

Expected: True
But was:  False

The question/debate about my code above:

Is am I testing one thing?

I am asserting on 4 different comparison methods within the object which could be argued as testing 4 things not one. The counter is greater than is greater than is greater than and that all of the methods which make that evaluation should be consistent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文