迷惘的码农。
分类:
2008-04-07 09:30:51
你以获悉如何应用单元测试来测试你的代码?但是如何测试你的测试?如何找到尚未被测试的代码——或者换句话说,测试尚未覆盖的代码?如何读两侧是完整性?所有这些问题都能通过一个称为代码覆盖率分析的实践来解答。当测试运行时代码覆盖率分析让你明了产品代码的哪些部分被执行了。
PHPUnit的代码覆盖率分析利用扩展提供的语句覆盖率功能。举个关于语句覆盖率表示什么的例子,如果某方法有100行代码,而且运行测试时实际只执行了其中的75行,那么认为该方法的代码覆盖率是75%。
让我们为中的类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.
显示一个来自代码覆盖率报告的节选。当运行测试时,执行过的代码行被绿色高亮显示,可执行但并为执行的代码行被红色高亮显示,而“无用代码”悲橙色高亮显示。实际代码行左侧的数字指示有多少涉及那一行。
图表 14.1.setBalance()
的代码覆盖率
单击一个被覆盖到的行的行号会打开一个面板(见),它显示涉及该行的测试用例。
图表 14.2. 带涉及测试信息的面板
我们的BankAccount
范例的代码覆盖率报告显示,我们尚无任何以合法值调用setBalance()
、depositMoney()
和withdrawMoney()
等方法的测试。
显示一个可被加入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());
}
}
?>
显示带有补充测试的setBalance()
方法的代码覆盖率。
图表 14.3. 带有补充测试的setBalance()
的代码覆盖率
注解@covers
可被用于测试代码中以指明测试方法要测试哪些方法。如果规定了,则只考虑指定的方法的代码覆盖率信息。显示一个例子。
范例 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
注解达到这个目的,如中所示。
范例 14.3: 使用@codeCoverageIgnoreStart
和@codeCoverageIgnoreEnd
注解
class Sample
{
// ...
public function doSomething()
{
if (0) {
// @codeCoverageIgnoreStart
$this->doSomethingElse();
// @codeCoverageIgnoreEnd
}
}
// ...
}
?>
注解@codeCoverageIgnoreStart
和@codeCoverageIgnoreEnd
之间的代码行被作为已执行(如果它们是可执行的)且不会被高亮显示。
缺省情况下,所有至少包含一行已执行的代码的源文件(而且只有这些文件)都会被包括在报表中。你可用PHPUnit_Util_Filter
API(见)配置被包含在报表中的源文件。
表 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自己的和测试的所有源文件预填充的。当白名单为空时(缺省),黑名单被使用。当白名单不空时,白名单被使用。