许多测试用例都涵盖了一个函数phpunit


Many test cases to cover a function - phpunit

对于下面的函数,我需要写更多的测试用例,我已经写了一个,有人能给出一些想法吗,也许是为了测试中间函数调用的返回值。

 public function calculateShortestPath($graphObj, $start, $destination)
{
    $shortestPath = null;
    if ($this->validateParams($graphObj, $start, $destination) == true) {
        $result = $this->getAllVerticesAndNeighbours($graphObj);
        $vertices = $result[self::VERTICES];
        $neighbours = $result[self::NEIGHBOURS];
        $vertexCost = array_fill_keys($vertices, self::INFINITY);
        $visitedVertices = array_fill_keys($vertices, null);
        $vertexCost[$start] = 0;
        $vertexQueue = $vertices;
        $result = $this->getShortestPath($vertexQueue, $vertexCost, $neighbours, $visitedVertices, $destination);
        $vertexCost = $result[self::VERTEX_COST];
        $shortestPathVertices = $result[self::SHORTEST_PATH_VERTICES];
        $path = $this->getRefinedShortestPath($shortestPathVertices, $destination);
        $shortestPath = new ShortestPath($path, $vertexCost[$destination]);
    }
    return $shortestPath;
}

我已经写了以下案例,

 /**
 * Test for calculateShortestPath function
 *
 * @param string|int $start starting point
 * @param string|int $destination destination point
 * @param array $expectedShortestPath expected shortest path
 * @param int|float $expectedCost expected cost
 * @dataProvider testCalculateShortestPathDataProvider
 */
public function testCalculateShortestPath($start, $destination, $expectedShortestPath, $expectedCost)
{
    $actualResult = $this->shortestPathCalc->calculateShortestPath($this->graph, $start, $destination);
    /* @var $actualResult ShortestPath */
    $this->assertEquals(
        $expectedShortestPath,
        $actualResult->getPath(),
        sprintf('Incorrect shortest path from %d to %d !', $start, $destination)
    );
    $this->assertEquals(
        $expectedCost,
        $actualResult->getCost(),
        sprintf('Incorrect shortest path cost from %d to %d !', $start, $destination)
    );
}

一般来说,单元测试应该表现出两个特性:

  • 每次测试都应该测试一件事
  • 每个测试都应该尽可能地愚蠢

第一个特性的原因是,如果测试失败,它将记录触发失败的测试用例方法。如果这种方法测试了很多东西,那么确定确切的失败就更麻烦了。

第二个特征的存在是因为每次测试失败,一定会出现问题。这个问题只能存在于两个地方中的一个(忽略PHP及其扩展中的错误,或者单元测试人员中的错误):测试中的代码,或者测试本身。"聪明"的测试会让你很难确定是哪种情况,而且你不想花一两个小时在你的课堂上寻找一个错误,当它实际上是错误的测试时。

在上面的例子中,您当前的测试非常好,但它打破了第一条规则(同时进行两个测试)。除非运行测试中的方法真的很昂贵,否则这个测试用例可能值得两次,第一次运行断言预期的最短路径,第二次运行断言预计的成本(如果你的方法确实有昂贵的运行时间,那么就有动机尝试优化它:)。

您的测试也可能违反第二条规则,因为我不知道$this->图是什么,也不知道它是如何设置的。这是一个实际的业务对象还是只是它的一个模型?您可能想了解PHPUnit的嘲讽和存根功能。

关于测试策略,有两种通用方法-黑盒测试(根据单元的规范测试单元,但将其视为对其内部工作一无所知)和玻璃盒测试(利用单元内部工作的知识设计测试)。我喜欢的方法是主要采用黑盒策略,围绕规范构建测试,然后一旦我完全覆盖了规范,就转向玻璃盒策略,编写额外的测试,覆盖黑盒测试没有使用的任何代码路径。

测试通常是关于边界的,比如在测试对输入的响应时,输入既有效又无效。因此,对于类中的每个方法,您都需要一个典型的案例(通常称为"快乐路径")来演示典型的用法,一些极端但仍然有效的输入,刚好在有效范围之外的输入范围(如果你的方法超出了1-10范围内的数字,那么一个0的测试用例和一个11的测试用例将涵盖这些情况),以及一个数据严重超出有效范围的测试用例。编程中的许多错误都发生在有效和无效输入之间的转换过程中(一次错误可能是最臭名昭著的例子),因此您的测试应该完全覆盖这些区域。

黑盒测试的一个好处是,如果你知道规格,你可以在有任何代码需要测试之前编写测试。然后,您可以开始实现代码,对其进行测试,针对失败的测试对其进行更正,并重复此过程,直到获得100%的通过率。这就是所谓的测试驱动开发。