【问题标题】:How to write similar test cases without breaking the Single Responsibility Principle?如何在不违反单一职责原则的情况下编写类似的测试用例?
【发布时间】:2016-08-10 14:44:23
【问题描述】:

我为不同的输入创建了一个单元测试,以确保输出正确。

[TestMethod]
    public void CountInversionTest()
    {
        #region Arrange
        int[] sourceArray = {4, 3, 2, 1};
        int correctInversionCount = 6;
        int[] sourceArray2 = { 1, 3, 5, 2, 4, 6};
        int correctInversionCount2 = 3;
        int[] sourceArray3 = { 5, 6, 2, 3, 1, 4, 7 };
        int correctInversionCount3 = 10;
        #endregion

        #region Act
        Sorter sorter = new Sorter();
        int inversionCount = sorter.CountInversion(sourceArray);
        int inversionCount2 = sorter.CountInversion(sourceArray2);
        int inversionCount3 = sorter.CountInversion(sourceArray3);
        #endregion

        #region Assert
        Assert.AreEqual(correctInversionCount, inversionCount);
        Assert.AreEqual(correctInversionCount2, inversionCount2);
        Assert.AreEqual(correctInversionCount3, inversionCount3);
        #endregion
    }

因为案例非常相似,所以我把它们放在一个测试方法中。这种行为可以吗,还是违反了单一责任原则?如果它破坏了 SRP,有什么更好的解决方案?

【问题讨论】:

  • 您通常希望将它们放在单独的测试中,以便更容易知道哪个测试失败。
  • @itsme86 我应该为每个案例创建一个不同的 TestMethod 吗?如果是这样,这些 TestMethod 的正确命名约定应该是什么?那不是在重复我自己吗?
  • 为什么要测试3个案例?他们中的一个人是否测试过任何特殊的边缘情况?如果是这样,请将目的放在单独的测试用例的名称中。
  • 如果您在所有 3 种情况下都在做同样的事情,测试框架通常会让您用属性装饰方法以传入参数。例如,测试方法可以接受一个数组,而属性将定义要传入的数组。例如,检查一下:github.com/nunit/docs/wiki/TestCase-Attribute

标签: c# unit-testing tdd solid-principles single-responsibility-principle


【解决方案1】:

使用像xUnit.net 这样的适当单元测试框架,您可以改为编写Parameterized Test

[Theory]
[InlineData(new[] { 4, 3, 2, 1 }, 6)]
[InlineData(new[] { 1, 3, 5, 2, 4, 6 }, 3)]
[InlineData(new[] { 5, 6, 2, 3, 1, 4, 7 }, 10)]
public void ParameterizedCountInversionTest(int[] input, int expected)
{
    Sorter sut = new Sorter();
    var actual = sut.CountInversion(input);
    Assert.Equal(expected, actual);
}

这将运行三个测试,而不是一个,让您更好地了解哪个特定测试用例失败(如果有失败)。

这样的测试也更具可读性。

NUnit 也有这个功能,但是 MSTest 没有(我上次看的时候)。

【讨论】:

    【解决方案2】:

    单一职责原则告诉我们,这种方法应该只有一个改变的理由。所以单元测试应该测试一种方法,并且应该只在被测方法改变时改变。这就是CountInversion() 方法。 CountInversion() 方法是否可以更改为sourceArray 输入之一必须更改,而其他输入则不必更改?在这种情况下,应将输入拆分为单独的测试以适应 SRP。

    一般来说,一个单元测试可以使用不同的输入多次调用被测方法。正如@itsme86 评论的那样,测试框架通常通过将参数传递给单元测试来促进这种行为。

    【讨论】:

      【解决方案3】:

      我决定回答我自己的问题

      所以我没有使用任何第三方框架或库进行测试,而是使用默认的 MSTest。 我最终这样做了

          [TestMethod]
          public void CountInversionTestCase1()
          {
              CountInversionTest(new int[] { 4, 3, 2, 1 }, 6);
          }
          [TestMethod]
          public void CountInversionTestCase2()
          {
              CountInversionTest(new int[] { 1, 3, 5, 2, 4, 6 }, 3);
          }
          [TestMethod]
          public void CountInversionTestCase3()
          {
              CountInversionTest(new int[] { 5, 6, 2, 3, 1, 4, 7 }, 10);
          }
      
          public void CountInversionTest(int[] sourceArray, int expectedInversionCount)
          {
              #region Act
              Sorter sorter = new Sorter();
              long actualInversionCount = sorter.CountInversion(sourceArray);
              #endregion
      
              #region Assert
              Assert.AreEqual(expectedInversionCount, actualInversionCount);
              #endregion
          }
      

      这不是最好的解决方案,但它满足要求并且不使用任何第三方库。 希望对大家有所帮助。

      【讨论】:

      • 最好给测试方法起一个有意义的名字,而不是Case1Case2Case3。给他们起为什么三个案例是必要的名字,即CountInversion()方法的哪一部分覆盖了每个案例没有被其他案例覆盖。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-10
      相关资源
      最近更新 更多