開始寫新的測試用例類時,可能想從寫下空測試方法開始,比如:
public function testSomething(): void
{
}
以此來跟蹤需要編寫的測試??諟y試的問題是 PHPUnit 框架會將它們解讀為成功。這種錯誤解讀導致錯誤報告變得毫無用處——無法分辨出測試是真的成功了還是根本就未編寫實現。在未實現的測試中調用 ?$this->fail()
? 同樣沒啥幫助,因為測試將被解讀為失敗。這和將未實現的測試解讀為成功是一樣的錯誤。
假如把成功的測試視為綠燈、測試失敗視為紅燈,那么還額外需要黃燈來將測試標記為未完成或尚未實現。?PHPUnit\Framework\IncompleteTest
? 是一個標記接口,用于將測試方法拋出的異常標記為測試未完成或目前尚未實現而導致的結果。?PHPUnit\Framework\IncompleteTestError
? 是這個接口的標準實現。
示例 7.1 展示了一個測試用例類 ?SampleTest
?,它有一個測試方法 ?testSomething()
?。通過在測試方法中調用便捷方法 ?markTestIncomplete()
(會自動拋出一個 ?PHPUnit\Framework\IncompleteTestError
? 異常)將這個測試標記為未完成。
示例 7.1 將測試標記為不完整
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class SampleTest extends TestCase
{
public function testSomething(): void
{
// 可選:如果愿意,在這里隨便測試點什么。
$this->assertTrue(true, 'This should already work.');
// 在這里停止,并將此測試標記為未完成。
$this->markTestIncomplete(
'This test has not been implemented yet.'
);
}
}
在 PHPUnit 命令行測試執(zhí)行器的輸出中,未完成的測試記為 ?I
?,如下例所示:
$ phpunit --verbose SampleTest
PHPUnit latest.0 by Sebastian Bergmann and contributors.
I
Time: 0 seconds, Memory: 3.95Mb
There was 1 incomplete test:
1) SampleTest::testSomething
This test has not been implemented yet.
/home/sb/SampleTest.php:12
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 1, Incomplete: 1.
表格 7.1 列舉了用于將測試標記為未完成的 API。
表格 7.1 用于不完整的測試的 API
方法 | 含義 |
void markTestIncomplete() ? |
將當前測試標記為未完成。 |
void markTestIncomplete(string $message) ? |
將當前測試標記為未完成。并用 ?$message ? 作為說明信息。 |
并非所有測試都能在任何環(huán)境中運行。比如說,考慮這樣一種情況:一個數據庫抽象層,針對其所支持的各種數據庫系統(tǒng)有多個不同的驅動程序。針對 MySQL 驅動程序的測試只在 MySQL 服務器可用才能運行。
示例 7.2 展示了一個測試用例類 ?DatabaseTest
?,它有一個測試方法 ?testConnection()
?。在測試用例類的 ?setUp()
? 模板方法中,檢查了 MySQLi 擴展是否可用,并且在擴展不可用時用 ?markTestSkipped()
? 方法來跳過此測試。
示例 7.2 跳過測試
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
final class DatabaseTest extends TestCase
{
protected function setUp(): void
{
if (!extension_loaded('mysqli')) {
$this->markTestSkipped(
'The MySQLi extension is not available.'
);
}
}
public function testConnection(): void
{
// ...
}
}
在 PHPUnit 命令行測試執(zhí)行器的輸出中,被跳過的測試記為 ?S
?,如下例所示:
$ phpunit --verbose DatabaseTest
PHPUnit latest.0 by Sebastian Bergmann and contributors.
S
Time: 0 seconds, Memory: 3.95Mb
There was 1 skipped test:
1) DatabaseTest::testConnection
The MySQLi extension is not available.
/home/sb/DatabaseTest.php:9
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 0, Skipped: 1.
表格 7.2 列舉了用于跳過測試的 API。
表格 7.2 用于跳過測試的 API
方法 | 含義 |
?void markTestSkipped() ? |
將當前測試標記為已跳過。 |
?void markTestSkipped(string $message) ? |
將當前測試標記為已跳過,并用 ?$message ? 作為說明信息。 |
除了上述方法,還可以用 ?@requires
? 標注來表達測試用例的一些常見前提條件。
表格 7.3 可能的 @requires 用法
類型 | 可能值 | 示例 | 其他示例 |
PHP | 任意PHP版本號以及可選的運算符 | @requires PHP 7.1.20 | @requires PHP >= 7.2 |
PHPUnit | 任意PHPUnit版本號以及可選的運算符 | @requires PHPUnit 7.3.1 | @requires PHPUnit < 8 |
OS | 與 PHP_OS 匹配的正則表達式 | @requires OS Linux | @requires OS WIN32|WINNT |
OSFAMILY | 任意 OS family | @requires OSFAMILY Solaris | @requires OSFAMILY Windows |
function | 任意 function_exists 的有效參數 | @requires function imap_open | @requires function ReflectionMethod::setAccessible |
extension | 任意擴展名以及可選的版本號和可選的運算符 | @requires extension mysqli | @requires extension redis >= 2.2.0 |
PHP、PHPUnit 和擴展的版本約束支持以下運算符:?<
?、?<=
?、?>
?、?>=
?、?=
?、?==
?、?!=
?、?<>
?。
版本是用 PHP 的 version_compare 函數進行比較的。除了其他事情之外,這意味著 ?=
? 和 ?==
? 運算符只能用于完整的 ?X.Y.Z
? 版本號,只用 ?X.Y
? 是不行的。
示例 7.3 用 @requires 跳過測試
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
/**
* @requires extension mysqli
*/
final class DatabaseTest extends TestCase
{
/**
* @requires PHP >= 5.3
*/
public function testConnection(): void
{
// 測試需要 mysqli 擴展,并且要求 PHP >= 5.3
}
// ... 其他需要 mysqli 擴展的測試
}
更多建議: