代码之家  ›  专栏  ›  技术社区  ›  Jeff Yates

如何对复杂方法进行单元测试

  •  25
  • Jeff Yates  · 技术社区  · 17 年前

    我有一个方法,给定一个北角和一个方位角,从8个可能的值(北、东北、东等)返回一个罗盘点值。我想创建一个单元测试,该测试提供了该方法的适当覆盖范围,为North和Bearing提供了不同的值,以确保我有足够的覆盖范围,使我相信我的方法是有效的。

    我最初的尝试为North生成了从-360到360的所有可能的整数值,并测试了从-360到360的每个方位值。然而,我的测试代码最终成为我正在测试的代码的另一个实现。这让我想知道最好的测试是什么,这样我的测试代码就不会像我的生产代码那样包含同样的错误。

    • 如何在不重新实现方法的情况下测试方法?

    显然,不要过多地讨论我的具体示例,因为这适用于许多需要测试复杂计算和数据范围的情况。

    10 回复  |  直到 17 年前
        1
  •  30
  •   Bill the Lizard    17 年前

    首先,你是对的 应该 包含一组特定的输入,这些输入具有预先计算的预期输出值。

    最后,在您的特定情况下,您可能需要更多的边缘情况,以确保您的函数正确处理有效输入范围内的“切换”点。这些将是输入数据中的点,在这些点上,输出将从“北”切换到“西北”,从“西北”切换到“西”,等等(不要运行代码来查找这些点,而是手动计算)。

        2
  •  5
  •   ConcernedOfTunbridgeWells    17 年前

    您可以将该方法重新分解为更易于单元测试的部分,并为这些部分编写单元测试。然后,整个方法的单元测试只需要关注集成问题。

        3
  •  5
  •   S.Lott    17 年前

    我更喜欢做以下事情。

    1. 创建具有正确答案的电子表格。无论它需要多么复杂,都是无关紧要的。您只需要一些包含案例的列和一些包含预期结果的列。

      以你为例,这可能很大。但大的没关系。您将获得一个角度、一个方位和结果罗盘点值。你可能会得到一些中间结果。

    2. 创建一个小程序,读取电子表格并编写简化的底线单元测试用例。你想把你的箱子拆下来

      def testCase215n( self ):
          self.fixture.setCourse( 215 )
          self.fixture.setBearing( 45 )
          self.fixture.calculate()
          self.assertEquals( "N", self.fixture.compass() )
      

    [这就是Python,同样的想法也适用于C.]

    电子表格包含唯一权威的正确答案列表。您可以由此生成一次或两次代码。当然,除非您在电子表格版本中发现错误,并且必须修复该错误。

    我使用一个带有xlrd和Mako模板生成器的小型Python程序来实现这一点。你可以用C#产品做类似的事情。

        4
  •  4
  •   dsimcha    17 年前

    如果你能想出一个 完全不同 完全不同 你可以在一些地方隐藏bug,然后进行测试。我经常这样做,当我有一个有效的,但复杂的实现的东西,可以实现得更简单,但效率低下。例如,如果编写一个哈希表实现,我可能会实现一个基于线性搜索的关联数组来测试它,然后使用大量随机生成的输入进行测试。线性搜索AA很难搞砸,甚至更难搞砸,以至于它是错误的 相同的 就像哈希表一样。因此,如果哈希表具有与线性搜索AA相同的可观察行为,我很有信心它是正确的。

        5
  •  3
  •   Seiti    17 年前

    我相信您的解决方案很好,尽管使用了XML文件(我会使用纯文本文件)。但更常用的策略是只测试极限情况,比如在您的案例中,使用输入值-360、360、-361、361和0。

        6
  •  1
  •   moffdub    17 年前

    你可以试试 orthogonal array testing 以实现所有对覆盖,而不是所有可能的组合。这是一种统计技术,基于这样一种理论,即大多数错误都是由于参数对之间的交互而产生的。它可以大大减少您编写的测试用例的数量。

        7
  •  1
  •   old_timer    17 年前

    您将很难不重新编写代码来测试它,这取决于您如何测试它。理想情况下,您希望独立的一方基于相同的需求编写测试代码,但不查看或借用您的代码。在大多数情况下,这不太可能发生。在这种情况下,这可能是矫枉过正。

    在这个特定的例子中,我将按-360到+360的顺序输入每个数字,并打印数字和结果(以一种可以作为头文件编译到另一个程序中的格式打印到文本文件中)。目视检查方向是否在所需输入处改变。这应该易于目视检查和验证。现在您有了一个输入和有效输出的表。接下来,让一个程序从有效输入中随机选择,并将其输入测试代码中,然后查看正确答案。做几百次这样的随机测试。在某个时刻,您需要验证小于-360或大于+360的数字是否按照您的要求进行处理,我假设是剪切还是调制。

        8
  •  0
  •   jayrod jayrod    17 年前

    link text 基本上你想要的是识别输入的类别。。都是实数吗?所有整数,只有正整数,只有负整数,等等。。。然后将输出操作分组。360与359有着独特的区别,或者他们最终对应用程序做了相同的事情。一旦有输入,就要将输入与输出进行组合。

        9
  •  0
  •   Silverfish    17 年前

    一种方法,可能是与其他测试方法结合使用的一种方法,是查看是否可以生成一个函数来反转正在测试的方法。在这种情况下,它将采用一个指南针方向(比如说东北方向),并输出一个方位(给定北向的方位)。然后,您可以通过将该方法应用于一系列输入来测试该方法,然后应用该函数来反转该方法,并查看是否返回原始输入。

    存在复杂情况,特别是当一个输出对应于多个输入时,但在这些情况下,可能会生成对应于给定输出的输入集,并测试该集的每个成员(或该集元素的某个样本)。

    这种方法的优点是,它不依赖于您能够手动模拟该方法,或者创建该方法的替代实现。如果反转涉及到与原始方法不同的问题处理方法,则应降低在这两种方法中犯相同错误的风险。

        10
  •  0
  •   Olie    17 年前

    伪代码:

    array colors = { red, orange, yellow, green, blue, brown, black, white }
    for north = -360 to 361
        for bearing = -361 to 361
            theColor = colors[dirFunction(north, bearing)] // dirFunction is the one being tested
            setColor (theColor)
            drawLine (centerX, centerY,
                      centerX + (cos(north + bearing) * radius),
                      centerY + (sin(north + bearing) * radius))
            Verify Resulting Circle against rotated reference diagram.
    

    顺便说一句,这种技术是世界上最伟大的调试工具的一种变体:让计算机给你画一幅它认为自己在做什么的图片。(开发人员常常浪费时间去追求什么 他们 想想电脑在做什么,却发现它在做完全不同的事情。)

    推荐文章