代码之家  ›  专栏  ›  技术社区  ›  Finglas

大型方法的单元测试

  •  6
  • Finglas  · 技术社区  · 16 年前

    我最近实现了一个算法(a*),它需要一个干净的接口。通过clean,我只需要几个属性和一个搜索方法。

    我发现很难测试搜索方法。它包含大约五个步骤,但我不得不一次性编写这个方法,这让事情变得很困难。

    对此有什么建议吗?

    编辑

    我用的是C。不,我现在手头没有密码。我的问题在于,只有在实现了整个搜索方法之后,测试才会通过,而不是算法中的一个步骤。之后我自然地重构了代码,但我发现很难实现。

    5 回复  |  直到 16 年前
        1
  •  14
  •   FinnNk    16 年前

    如果你的步骤足够大(或者在他们自己的意义上有意义),你应该考虑将它们委托给其他较小的类,并测试类和它们之间的交互。例如,如果您有一个解析步骤,后面跟着一个排序步骤,后面跟着一个搜索步骤,那么拥有一个解析器类、一个排序器类等可能是有意义的。然后,您将在这些步骤中的每一个上使用TDD。

    不知道您使用的是哪种语言,但是如果您在.net世界中,您可以将这些类设置为内部类,然后将它们暴露给您的测试类,并使用“内部可见”将它们隐藏起来。

    如果这些步骤本身是小而无意义的,那么tvanfosson的建议就是可行之道。

        2
  •  5
  •   tvanfosson    16 年前

    使现代化 :我还应该澄清,简单地重构到私有方法并不一定意味着您需要创建特定于这些方法的测试。如果要彻底测试依赖私有方法的公共方法,则可能不需要直接测试私有方法。这可能是一般情况。有时候,直接测试私有方法是有意义的(例如,当它简化或减少公共方法所需的测试用例的数量)时,但我不认为仅仅因为重构到私有方法实现就需要创建测试。

        3
  •  2
  •   Community Mohan Dere    9 年前

    使用TDD时需要记住的一件重要事情是 测试不需要永远存在。

    我对这种情况的建议是:

    • 根据新方法勾勒出大型方法的框架,这样您就有了一系列单独测试的步骤。
    • 然后,一旦你测试了所有的部件,写另一个测试 全部的
    • 在这一点上,这个大方法可以工作,并且已经被测试覆盖,现在您可以开始重构了。我推荐你,就像 FinnNk 删除测试 为了他们。

    很多困惑来自这样一种想法:一旦你写了一个测试,你就会 我总是要保留它 ,情况未必如此。然而,如果你是感性的,有一些方法可以对私有方法进行单元测试 can be achieved in Java ,也许C#中也有类似的结构。

        4
  •  1
  •   peter.murray.rust    16 年前

    我假设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的预期距离是多少。。。编写测试。将A添加到集合Z的效果是什么?需要一个测试。对于这个算法,您需要进行测试 heuristic_estimate_of_distance 等等这是一项艰巨的工作。

    还有一件事比这更糟糕——数值算法。对角化矩阵——我们真的得到了正确的答案吗。我和一位科学家一起写了三阶导数矩阵——这会让我害怕。。。

        5
  •  -1
  •   joel.neely    16 年前

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

    它允许您通过检查记录的数据来检查行为,而不是对参数和方法结果进行纯粹的黑盒式测试。

    免责声明:我听过关于Texttest的演示,看过文档,但还没有时间在一个严肃的应用程序中试用。