许多测试用例覆盖一个功能 - phpunit
Many test cases to cover a function - phpunit
下面的函数我需要写更多的测试用例,我已经写了一个,谁能给点想法,也许可以测试return个中间函数调用的值。
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 及其扩展中的错误,或在单元测试器中):在被测代码中,或在测试本身中。 "Clever" 测试将很难确定它是哪种情况,而且你不想花一两个小时来寻找 class 中的错误,结果发现它实际上是错误的测试.
在您上面的示例中,您当前的测试非常好,但它违反了第一条规则(同时进行了两个测试)。除非 运行 测试被测方法真的很昂贵,否则值得两次使用此测试用例,第一个 运行 断言预期的最短路径,第二个 运行 断言预期的成本(如果您的方法确实有昂贵的 运行 时间,然后就有动力去尝试优化它 :) )。
您的测试也可能违反第二条规则,因为我不知道 $this -> graph 是什么或它是如何设置的。这是一个实际的业务对象还是它的模型?您可能想查看 PHPUnit 的模拟存根功能。
关于测试策略,有两种通用方法 - 黑盒测试(您根据规范测试一个单元,但就像您不了解其内部工作原理一样对待它)和玻璃盒测试(您使用您的知识单位的内部运作来设计测试)。我的首选方法是主要采用黑盒策略,围绕规范构建测试,然后一旦我完全覆盖了规范,就转向玻璃盒策略来编写额外的测试,这些测试将覆盖黑盒测试的任何代码路径不锻炼。
测试通常与边界有关,例如在输入有效和无效时测试对输入的响应。因此,对于您的 class 拥有的每种方法,您都需要一个典型案例(通常称为 "happy path")来演示典型用法、一些极端但仍然有效的输入、一系列超出范围的输入有效范围(如果您的方法不包括 1-10 范围内的数字,则 0 的测试用例和 11 的测试用例将涵盖这些情况)和数据严重超出有效范围的测试用例。编程中的许多错误发生在有效输入和无效输入之间的转换(差一错误可能是最臭名昭著的例子),因此您的测试应该彻底覆盖这些区域。
黑盒测试的一个好处是,如果您了解规格,则可以在有任何代码进行测试之前编写测试。然后你可以开始实施代码,测试它,纠正失败的测试并重复这个过程,直到你获得 100% 的通过率。这称为测试驱动开发。
下面的函数我需要写更多的测试用例,我已经写了一个,谁能给点想法,也许可以测试return个中间函数调用的值。
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 及其扩展中的错误,或在单元测试器中):在被测代码中,或在测试本身中。 "Clever" 测试将很难确定它是哪种情况,而且你不想花一两个小时来寻找 class 中的错误,结果发现它实际上是错误的测试.
在您上面的示例中,您当前的测试非常好,但它违反了第一条规则(同时进行了两个测试)。除非 运行 测试被测方法真的很昂贵,否则值得两次使用此测试用例,第一个 运行 断言预期的最短路径,第二个 运行 断言预期的成本(如果您的方法确实有昂贵的 运行 时间,然后就有动力去尝试优化它 :) )。
您的测试也可能违反第二条规则,因为我不知道 $this -> graph 是什么或它是如何设置的。这是一个实际的业务对象还是它的模型?您可能想查看 PHPUnit 的模拟存根功能。
关于测试策略,有两种通用方法 - 黑盒测试(您根据规范测试一个单元,但就像您不了解其内部工作原理一样对待它)和玻璃盒测试(您使用您的知识单位的内部运作来设计测试)。我的首选方法是主要采用黑盒策略,围绕规范构建测试,然后一旦我完全覆盖了规范,就转向玻璃盒策略来编写额外的测试,这些测试将覆盖黑盒测试的任何代码路径不锻炼。
测试通常与边界有关,例如在输入有效和无效时测试对输入的响应。因此,对于您的 class 拥有的每种方法,您都需要一个典型案例(通常称为 "happy path")来演示典型用法、一些极端但仍然有效的输入、一系列超出范围的输入有效范围(如果您的方法不包括 1-10 范围内的数字,则 0 的测试用例和 11 的测试用例将涵盖这些情况)和数据严重超出有效范围的测试用例。编程中的许多错误发生在有效输入和无效输入之间的转换(差一错误可能是最臭名昭著的例子),因此您的测试应该彻底覆盖这些区域。
黑盒测试的一个好处是,如果您了解规格,则可以在有任何代码进行测试之前编写测试。然后你可以开始实施代码,测试它,纠正失败的测试并重复这个过程,直到你获得 100% 的通过率。这称为测试驱动开发。