第 14 章 代码覆盖率分析
时间:2008-04-07 来源:hshq_cn
第 14 章 代码覆盖率分析
你以获悉如何应用单元测试来测试你的代码?但是如何测试你的测试?如何找到尚未被测试的代码——或者换句话说,测试尚未覆盖的代码?如何读两侧是完整性?所有这些问题都能通过一个称为代码覆盖率分析的实践来解答。当测试运行时代码覆盖率分析让你明了产品代码的哪些部分被执行了。
PHPUnit的代码覆盖率分析利用
Xdebug
扩展提供的语句覆盖率功能。举个关于语句覆盖率表示什么的例子,如果某方法有100行代码,而且运行测试时实际只执行了其中的75行,那么认为该方法的代码覆盖率是75%。
让我们为
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#test-first-programming.bankaccount-example.examples.BankAccount2.php]范例 13.3[/url]
中的类BankAccount生成一个代码覆盖率报告。
phpunit --coverage-html ./report BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
Generating report, this may take a moment.
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage.png]图形 14.1[/url]
显示一个来自代码覆盖率报告的节选。当运行测试时,执行过的代码行被绿色高亮显示,可执行但并为执行的代码行被红色高亮显示,而“无用代码”悲橙色高亮显示。实际代码行左侧的数字指示有多少涉及那一行。
图表 14.1.setBalance()的代码覆盖率
单击一个被覆盖到的行的行号会打开一个面板(见
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage2.png]图表 14.2[/url]
),它显示涉及该行的测试用例。
图表 14.2. 带涉及测试信息的面板
我们的BankAccount范例的代码覆盖率报告显示,我们尚无任何以合法值调用setBalance()、depositMoney()和withdrawMoney()等方法的测试。
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.examples.BankAccountTest.php]范例 14.1[/url]
显示一个可被加入BankAccountTest测试用例类以完全覆盖类BankAccount的测试。
范例 14.1: 达到完全代码覆盖率所缺失的测试
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
// ...
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage3.png]图表 14.3[/url]
显示带有补充测试的setBalance()方法的代码覆盖率。
图表 14.3. 带有补充测试的setBalance()的代码覆盖率
指定覆盖的方法
注解@covers可被用于测试代码中以指明测试方法要测试哪些方法。如果规定了,则只考虑指定的方法的代码覆盖率信息。
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.specifying-covered-methods.examples.BankAccountTest.php]范例 14.2[/url]
显示一个例子。
范例 14.2: 一些指明了要覆盖哪些方法的测试
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
protected $ba;
protected function setUp()
{
$this->ba = new BankAccount;
}
/**
* @covers BankAccount::getBalance
*/
public function testBalanceIsInitiallyZero()
{
$this->assertEquals(0, $this->ba->getBalance());
}
/**
* @covers BankAccount::withdrawMoney
*/
public function testBalanceCannotBecomeNegative()
{
try {
$this->ba->withdrawMoney(1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::depositMoney
*/
public function testBalanceCannotBecomeNegative2()
{
try {
$this->ba->depositMoney(-1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::getBalance
* @covers BankAccount::depositMoney
* @covers BankAccount::withdrawMoney
*/
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
忽略代码块
有时候你会有些无法测试的代码块,而且你可能想在代码覆盖率分析时忽略它们。PHPUnit允许你利用@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd注解达到这个目的,如
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.ignoring-code-blocks.examples.Sample.php]范例 14.3[/url]
中所示。
范例 14.3: 使用@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd注解
class Sample
{
// ...
public function doSomething()
{
if (0) {
// @codeCoverageIgnoreStart
$this->doSomethingElse();
// @codeCoverageIgnoreEnd
}
}
// ...
}
?>
注解@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd之间的代码行被作为已执行(如果它们是可执行的)且不会被高亮显示。
包含和排除文件
缺省情况下,所有至少包含一行已执行的代码的源文件(而且只有这些文件)都会被包括在报表中。你可用PHPUnit_Util_Filter API(见
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.including-excluding-files.tables.filter-api]表 14.1[/url]
)配置被包含在报表中的源文件。
表 14.1. PHPUnit_Util_Filter API
方法含义void addDirectoryToFilter(string $directory)将某目录中的所有以.php为后缀的文件加入黑名单(递归的)。void addDirectoryToFilter(string $directory, string $suffix)将某目录中的所有以$suffix为后缀的文件加入黑名单(递归的)。void addFileToFilter(string $filename)将一个文件加入黑名单。void removeDirectoryFromFilter(string $directory)从黑名单中删除所有来自某目录的以.php为后缀的文件(递归的)。void removeDirectoryFromFilter(string $directory, string $suffix)从黑名单中删除所有来自某目录的以$suffix为后缀的文件(递归的)。void removeFileFromFilter(string $filename)从黑名单中删除一个文件。void addDirectoryToWhitelist(string $directory)将某目录中的所有以.php为后缀的文件加入白名单(递归的)。void addDirectoryToWhitelist(string $directory, string $suffix)将某目录中的所有以$suffix为后缀的文件加入白名单(递归的)。void addFileToWhitelist(string $filename)将一个文件加入白名单。void removeDirectoryFromWhitelist(string $directory)从白名单中删除所有来自某目录的以.php为后缀的文件(递归的)。void removeDirectoryFromWhitelist(string $directory, string $suffix)从白名单中删除所有来自某目录的以$suffix为后缀的文件(递归的)。void removeFileFromWhitelist(string $filename)从白名单中删除一个文件。
黑名单是用PHPUnit自己的和测试的所有源文件预填充的。当白名单为空时(缺省),黑名单被使用。当白名单不空时,白名单被使用。
你以获悉如何应用单元测试来测试你的代码?但是如何测试你的测试?如何找到尚未被测试的代码——或者换句话说,测试尚未覆盖的代码?如何读两侧是完整性?所有这些问题都能通过一个称为代码覆盖率分析的实践来解答。当测试运行时代码覆盖率分析让你明了产品代码的哪些部分被执行了。
PHPUnit的代码覆盖率分析利用
Xdebug
扩展提供的语句覆盖率功能。举个关于语句覆盖率表示什么的例子,如果某方法有100行代码,而且运行测试时实际只执行了其中的75行,那么认为该方法的代码覆盖率是75%。
让我们为
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#test-first-programming.bankaccount-example.examples.BankAccount2.php]范例 13.3[/url]
中的类BankAccount生成一个代码覆盖率报告。
phpunit --coverage-html ./report BankAccountTest
PHPUnit 3.2.10 by Sebastian Bergmann.
...
Time: 0 seconds
OK (3 tests)
Generating report, this may take a moment.
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage.png]图形 14.1[/url]
显示一个来自代码覆盖率报告的节选。当运行测试时,执行过的代码行被绿色高亮显示,可执行但并为执行的代码行被红色高亮显示,而“无用代码”悲橙色高亮显示。实际代码行左侧的数字指示有多少涉及那一行。
图表 14.1.setBalance()的代码覆盖率
单击一个被覆盖到的行的行号会打开一个面板(见
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage2.png]图表 14.2[/url]
),它显示涉及该行的测试用例。
图表 14.2. 带涉及测试信息的面板
我们的BankAccount范例的代码覆盖率报告显示,我们尚无任何以合法值调用setBalance()、depositMoney()和withdrawMoney()等方法的测试。
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.examples.BankAccountTest.php]范例 14.1[/url]
显示一个可被加入BankAccountTest测试用例类以完全覆盖类BankAccount的测试。
范例 14.1: 达到完全代码覆盖率所缺失的测试
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
// ...
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.figures.Code_Coverage3.png]图表 14.3[/url]
显示带有补充测试的setBalance()方法的代码覆盖率。
图表 14.3. 带有补充测试的setBalance()的代码覆盖率
指定覆盖的方法
注解@covers可被用于测试代码中以指明测试方法要测试哪些方法。如果规定了,则只考虑指定的方法的代码覆盖率信息。
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.specifying-covered-methods.examples.BankAccountTest.php]范例 14.2[/url]
显示一个例子。
范例 14.2: 一些指明了要覆盖哪些方法的测试
require_once 'PHPUnit/Framework.php';
require_once 'BankAccount.php';
class BankAccountTest extends PHPUnit_Framework_TestCase
{
protected $ba;
protected function setUp()
{
$this->ba = new BankAccount;
}
/**
* @covers BankAccount::getBalance
*/
public function testBalanceIsInitiallyZero()
{
$this->assertEquals(0, $this->ba->getBalance());
}
/**
* @covers BankAccount::withdrawMoney
*/
public function testBalanceCannotBecomeNegative()
{
try {
$this->ba->withdrawMoney(1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::depositMoney
*/
public function testBalanceCannotBecomeNegative2()
{
try {
$this->ba->depositMoney(-1);
}
catch (BankAccountException $e) {
$this->assertEquals(0, $this->ba->getBalance());
return;
}
$this->fail();
}
/**
* @covers BankAccount::getBalance
* @covers BankAccount::depositMoney
* @covers BankAccount::withdrawMoney
*/
public function testDepositWithdrawMoney()
{
$this->assertEquals(0, $this->ba->getBalance());
$this->ba->depositMoney(1);
$this->assertEquals(1, $this->ba->getBalance());
$this->ba->withdrawMoney(1);
$this->assertEquals(0, $this->ba->getBalance());
}
}
?>
忽略代码块
有时候你会有些无法测试的代码块,而且你可能想在代码覆盖率分析时忽略它们。PHPUnit允许你利用@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd注解达到这个目的,如
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.ignoring-code-blocks.examples.Sample.php]范例 14.3[/url]
中所示。
范例 14.3: 使用@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd注解
class Sample
{
// ...
public function doSomething()
{
if (0) {
// @codeCoverageIgnoreStart
$this->doSomethingElse();
// @codeCoverageIgnoreEnd
}
}
// ...
}
?>
注解@codeCoverageIgnoreStart和@codeCoverageIgnoreEnd之间的代码行被作为已执行(如果它们是可执行的)且不会被高亮显示。
包含和排除文件
缺省情况下,所有至少包含一行已执行的代码的源文件(而且只有这些文件)都会被包括在报表中。你可用PHPUnit_Util_Filter API(见
[url=file:///F:/data2/PHPUnit_Pocket_Guide_-_v32/index_cn.html#code-coverage-analysis.including-excluding-files.tables.filter-api]表 14.1[/url]
)配置被包含在报表中的源文件。
表 14.1. PHPUnit_Util_Filter API
方法含义void addDirectoryToFilter(string $directory)将某目录中的所有以.php为后缀的文件加入黑名单(递归的)。void addDirectoryToFilter(string $directory, string $suffix)将某目录中的所有以$suffix为后缀的文件加入黑名单(递归的)。void addFileToFilter(string $filename)将一个文件加入黑名单。void removeDirectoryFromFilter(string $directory)从黑名单中删除所有来自某目录的以.php为后缀的文件(递归的)。void removeDirectoryFromFilter(string $directory, string $suffix)从黑名单中删除所有来自某目录的以$suffix为后缀的文件(递归的)。void removeFileFromFilter(string $filename)从黑名单中删除一个文件。void addDirectoryToWhitelist(string $directory)将某目录中的所有以.php为后缀的文件加入白名单(递归的)。void addDirectoryToWhitelist(string $directory, string $suffix)将某目录中的所有以$suffix为后缀的文件加入白名单(递归的)。void addFileToWhitelist(string $filename)将一个文件加入白名单。void removeDirectoryFromWhitelist(string $directory)从白名单中删除所有来自某目录的以.php为后缀的文件(递归的)。void removeDirectoryFromWhitelist(string $directory, string $suffix)从白名单中删除所有来自某目录的以$suffix为后缀的文件(递归的)。void removeFileFromWhitelist(string $filename)从白名单中删除一个文件。
黑名单是用PHPUnit自己的和测试的所有源文件预填充的。当白名单为空时(缺省),黑名单被使用。当白名单不空时,白名单被使用。
相关阅读 更多 +
排行榜 更多 +