单元测试大方法(Unit Testing a large method)

2019-09-01 03:14发布

下面的测试驱动开发是。

我最近实施,需要一个干净的界面算法(A *)。 通过清理所有我想要的是一对夫妇的属性和一个单一的搜索方法。

什么我发现很难为测试搜索方法。 它包含围绕五个步骤,但我基本上是被迫在一个大干一番,这使得事情变得困难编写这种方法。

对此有什么建议吗?

编辑

我使用C#。 不,我不手头此刻的代码。 我的问题依赖于事实测试只实现了整个搜索方法后经过 - 而不是在算法的一个步骤。 我自然重构的代码之后,但它正在执行,我发现很难。

Answer 1:

如果你的步骤是足够大的(或者是有意义的在自己的权利),你应该考虑他们委托给其他较小的类和测试类以及它们之间的相互作用。 例如,如果你有一个分析步骤,然后是分拣步骤,然后是搜索步骤可能是有意义的有一个解析器类,一类分选机等,您会再使用TDD每个那些。

不知道你正在使用的语言,但如果你在.NET世界是你可以把这些类内部,然后将它们暴露于测试类“内部可见”,这将让他们隐藏。

如果步骤是小的和无意义自己则tvanfosson的建议是要走的路。



Answer 2:

重构你的大法成更小的,私人的方法。 使用反射或其他机制可以在你的语言,独立测试更小的方法。 最坏的情况 - 如果你没有进入反射或朋友,使受保护的方法和具有主类的测试类继承,所以它可以访问它们。

更新 :我还要澄清,只是重构私有方法并不一定意味着你需要创建特定于这些方法测试。 如果你彻底的测试依赖于私有方法的公共方法,你可能并不需要直接测试的私有方法。 这可能是一般情况。 有些时候它确实是有意义的直接测试私有方法(说时,它可以简化或减少所需的公共方法测试用例的数量),但我不认为这是一个需要简单地创建测试,因为你重构一个私人方法实现。



Answer 3:

我假设A *意味着搜索算法(例如http://en.wikipedia.org/wiki/A*_search_algorithm )。 如果是这样,我明白你的问题,因为我们有类似的要求。 这里的WP算法,我将在下面评论:


伪代码

 function A*(start,goal)
     closedset := the empty set                 % The set of nodes already evaluated.     
     openset := set containing the initial node % The set of tentative nodes to be evaluated.
     g_score[start] := 0                        % Distance from start along optimal path.
     h_score[start] := heuristic_estimate_of_distance(start, goal)
     f_score[start] := h_score[start]           % Estimated total distance from start to goal through y.
     while openset is not empty
         x := the node in openset having the lowest f_score[] value
         if x = goal
             return reconstruct_path(came_from,goal)
         remove x from openset
         add x to closedset
         foreach y in neighbor_nodes(x)
             if y in closedset
                 continue
             tentative_g_score := g_score[x] + dist_between(x,y)

             if y not in openset
                 add y to openset

                 tentative_is_better := true
             elseif tentative_g_score < g_score[y]
                 tentative_is_better := true
             else
                 tentative_is_better := false
             if tentative_is_better = true
                 came_from[y] := x
                 g_score[y] := tentative_g_score
                 h_score[y] := heuristic_estimate_of_distance(y, goal)
                 f_score[y] := g_score[y] + h_score[y]
     return failure

 function reconstruct_path(came_from,current_node)
     if came_from[current_node] is set
         p = reconstruct_path(came_from,came_from[current_node])
         return (p + current_node)
     else
         return the empty path

闭集可以省略(得到树搜索算法)如果溶液是保证存在,或者如果算法被适配成使得新节点被添加到仅当它们具有比在任何先前的迭代较小的f值的开集。


首先,我不会轻率,这取决于你是否理解算法 - 这听起来如果你这样做的。 这也将是可能的抄写上述算法 - 希望它的工作),并给它一些测试。 那是因为我怀疑WP的作者是比我好,我会做什么! 大规模的测试将行使边缘的情况下,如没有节点,一个节点,两个节点+无棱,等...如果他们全部通过我睡高兴。 但是,如果他们失败了,没有选择,只能理解算法。

如果是这样,我认为你必须构建数据结构测试。 这些(至少)集,距离,记分等你要创建这些对象并对其进行测试。 什么是1,2,3的情况下预期的距离...编写测试。 什么是添加到集合Z的影响? 需要测试。 对于这个算法,你需要测试heuristic_estimate_of_distance等。 这是一个很大的工作。

一种方法可能是为了寻找另一种语言的实现和查询它查找数据结构中的值。 当然,如果你正在修改的算法,你是你自己!

有一件事比这更糟糕 - 数值算法。 矩阵对角化 - 我们究竟得到正确的答案。 我曾与一位科学家写第三衍生矩阵 - 这将让我害怕...



Answer 4:

需要记住的重要事情时,采用TDD是测试并不需要永远活着。

在你的榜样,你知道你要提供一个干净的界面,并且许多操作将被委托给私有方法。 我认为这是这些方法都为私有要创建的假设,并保持这种方式,导致最麻烦。 此外,一旦你已经开发他们的测试必须呆在身边的假设。

我对这种情况下的建议是:

  • 画出大方法的骨架中的新方法方面,所以你有一系列的步骤,这是有意义的单独测试。
  • 使这些新的方法public默认,并开始使用TDD来开发它们。
  • 然后,一旦你把所有的部件测试,再写其测试的整个方法。 确保如果有较小的,受委托的部分都打破了这种新的测试将会失败。
  • 此时的大方法的工作原理,并通过测试覆盖了,你现在可以开始重构。 我建议,作为FinnNk确实,试图在更小的方法提取到自己的类。 如果这样做没有意义,或者如果它需要太多的时间,我会推荐一些其他人可能不同意:改变小方法分为私房和删除测试他们。

其结果是,你已经使用TDD创建整个方法,仍然由测试覆盖,API正是你想要呈现的一个。

造成很多混乱,来自想法,一旦你写一个测试,你总是要保持它 ,而不是必然的情况。 但是,如果你是多情的,有办法让私有方法的单元测试可以在Java实现 ,或许还有在C#中类似的结构。



Answer 5:

你可能会考虑Texttest( http://texttest.carmen.se/ )作为测试“界面下”的一种方式。

它可以让你通过检查记录的数据进行验证的行为,而不是对参数和方法的结果纯粹的黑箱式的测试,以检查行为。

免责声明:我听说Texttest介绍,看了看文档,但尚未不得不尝试一下在一个重要的应用程序的时间。



文章来源: Unit Testing a large method