> 沒有經過測試的代碼不是完成的代碼。
對于敏捷開發來說,測試也是重要的一環。對于ThinkPHP和其上開發的應用我們需要進行單元測試。
PHPUnit 是一個面向程序員的PHP測試框架。它是一個xUnit架構的單元測試框架的實例。
# 運行條件
PHPUnit 4.6 需要 PHP 5.3.3,強烈推薦使用最新版本的 PHP。
PHPUnit 需要使用 [dom](http://php.net/manual/en/dom.setup.php) 和 [json](http://php.net/manual/en/json.installation.php) 擴展,它們通常是默認啟用的。
PHPUnit 還需要 [pcre](http://php.net/manual/en/pcre.installation.php)、[reflection](http://php.net/manual/en/reflection.installation.php)、[spl](http://php.net/manual/en/spl.installation.php) 擴展。自 5.3.0 開始 PHP 核心需要這些擴展,通常無法禁用。
代碼覆蓋率分析報告功能需要 [Xdebug](http://xdebug.org/) (2.1.3以上)與 [tokenizer](http://php.net/manual/en/tokenizer.installation.php) 擴展。生成 XML 格式的報告需要有 xmlwriter 擴展。
# PHP檔案包(PHAR)
要獲取 PHPUnit,最簡單的方法是下載 PHPUnit 的 PHP 檔案包 (PHAR),它將 PHPUnit 所需要的所有必要組件(以及某些可選組件)捆綁在單個文件中:
要使用 PHP檔案包(PHAR)需要有 [phar](http://php.net/manual/en/phar.installation.php) 擴展。
要使用 PHAR 的 --self-update 功能需要有 [openssl](http://php.net/manual/en/openssl.installation.php) 擴展。
如果啟用了 Suhosin 擴展,需要在 php.ini 中允許執行 PHAR:
`suhosin.executor.include.whitelist = phar`
注意
要從 <https://phar.phpunit.de/> 下載,需要[支持 TLS/SNI](http://en.wikipedia.org/wiki/Server_Name_Indication)的客戶端,例如 wget 1.14(或更高版本)。
如果要全局安裝 PHAR:
~~~
$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ sudo mv phpunit.phar /usr/local/bin/phpunit
$ phpunit --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.
~~~
也可以直接使用下載的 PHAR 文件:
~~~
$ wget https://phar.phpunit.de/phpunit.phar
$ php phpunit.phar --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.
~~~
## Windows
整體上說,在 Windows 下安裝 PHAR 和手工[在 Windows 下安裝 Composer](https://getcomposer.org/doc/00-intro.md#installation-windows) 是一樣的過程:
1. 為 PHP 的二進制可執行文件建立一個目錄,例如 C:\bin
2. 將 **;C:\bin** 附加到 PATH 環境變量中([相關幫助](http://stackoverflow.com/questions/6318156/adding-python-path-on-windows-7))
3. 下載 <https://phar.phpunit.de/phpunit.phar> 并將文件保存到 *C:\bin\phpunit.phar*
4. 打開命令行(例如,按 **Windows+R** ? 輸入 cmd ? **ENTER**)
5. 建立外包覆批處理腳本(最后得到 C:\bin\phpunit.cmd):
~~~
C:\Users\username> cd C:\bin
C:\bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
C:\bin> exit
~~~
6. 新開一個命令行窗口,確認一下可以在任意路徑下執行 PHPUnit:
~~~
C:\Users\username> phpunit --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.
~~~
對于 Cygwin 或 MingW32 (例如 TortoiseGit) shell 環境,可以跳過第五步。 取而代之的是,把文件保存為 phpunit (沒有 .phar 擴展名),然后用 `chmod 775 phpunit` 將其設為可執行。
# Composer
如果用 [Composer](https://getcomposer.org/) 來管理項目的依賴關系,只要在項目的 composer.json 文件中簡單地加上對 phpunit/phpunit 的依賴關系即可。下面是一個最小化的 composer.json 文件的例子,只定義了一個對 PHPUnit 4.6 的開發時(development-time)依賴:
~~~
{
"require-dev": {
"phpunit/phpunit": "4.6.*"
}
}
~~~
要通過 Composer 完成系統級的安裝,可以運行:
`composer global require "phpunit/phpunit=4.6.*"`
請確保 path 變量中包含有 `~/.composer/vendor/bin/`。
## 可選的組件包
有以下可選組件包可用:
- PHP_Invoker
一個工具類,可以用帶有超時限制的方式調用可調用內容。當需要在嚴格模式下保證測試的超時限制時,這個組件包是必須的。
PHPUnit 的 PHAR 分發中已經包含了此組件包。若要通過 Composer 安裝此組件包,添加如下 "require-dev" 依賴項:`"phpunit/php-invoker": "*"`
- DbUnit
移植到 PHP/PHPUnit 上的 DbUnit 用于提供對數據庫交互測試的支持。
PHPUnit 的 PHAR 分發中已經包含了此組件包。若要通過 Composer 安裝此組件包,添加如下 "require-dev" 依賴項:
`"phpunit/dbunit": ">=1.2"`
- PHPUnit_Selenium
將 Selenium RC 集成于 PHPUnit。
PHPUnit 的 PHAR 分發中已經包含了此組件包。若要通過 Composer 安裝此組件包,添加如下 "require-dev" 依賴項:
`"phpunit/phpunit-selenium": ">=1.2"`
# 開始寫測試
PHPUnit 編寫測試的基本慣例與步驟:
1. 針對類 Class 的測試寫在類 ClassTest中。
2. ClassTest(通常)繼承自 PHPUnit_Framework_TestCase。
3. 測試都是命名為 test* 的公用方法。
也可以在方法的文檔注釋塊(docblock)中使用 @test 標注將其標記為測試方法。
4. 在測試方法內,類似于 assertEquals()(參見 [附錄 A](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#appendixes.assertions))這樣的斷言方法用來對實際值與預期值的匹配做出斷言。
## 例子
~~~
<?php
class StackTest extends PHPUnit_Framework_TestCase{
public function testPushAndPop(){
$stack = array();
$this->assertEquals(0, count($stack));
array_push($stack, 'foo');
$this->assertEquals('foo', $stack[count($stack)-1]);
$this->assertEquals(1, count($stack));
$this->assertEquals('foo', array_pop($stack));
$this->assertEquals(0, count($stack));
}
}
?>
~~~
> 當你想把一些東西寫到 print 語句或者調試表達式中時,別這么做,將其寫成一個測試來代替。--Martin Fowler
## 注意點:
### 測試的依賴關系
理想的測試應當覆蓋程序的所有可能路徑。故此,我們可能為某一種場景做測試,這個場景里某些程序的調用有依賴順序,如點擊了A 成功響應事件B,然后B中又調用了C方法。
測試的時候肯定有A失敗的情況,而A失敗了不會走B和C的過程。
在測試里,我們往往單獨一個方法測試一個功能。A的處理,B的事件,C的事件。
并且默認是按照A B C的順序往下測試,但是如果對應現實場景,我們寫了ABC,但是BC 其實是依賴于A的成功結果才會執行的。
因此,我們可以通過 B 和 C 上寫 `@depends`依賴的方式,當A 失敗了 B和C 直接 也報失敗。 這樣的處理 符合自然也更高效。
~~~
<?php
class DependencyFailureTest extends PHPUnit_Framework_TestCase{
public function testOne(){
$this->assertTrue(FALSE);
}
/**
* @depends testOne
*/
public function testTwo(){
}
}
?>
~~~
測試結果:
~~~
phpunit --verbose DependencyFailureTest
PHPUnit 4.6.0 by Sebastian Bergmann and contributors.
FS
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) DependencyFailureTest::testOne
Failed asserting that false is true.
/home/sb/DependencyFailureTest.php:6
There was 1 skipped test:
1) DependencyFailureTest::testTwo
This test depends on "DependencyFailureTest::testOne" to pass.
FAILURES!
Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.
~~~
為了快速定位缺陷,我們希望把注意力集中于相關的失敗測試上。這就是為什么當某個測試所依賴的測試失敗時,PHPUnit 會跳過這個測試。通過利用測試之間的依賴關系,缺陷定位得到了改進。
### 命令行測試執行器
PHPUnit 命令行測試執行器可通過 phpunit 命令調用。下面的代碼展示了如何用 PHPUnit 命令行測試執行器來運行測試:
~~~
phpunit ArrayTest
PHPUnit 4.6.0 by Sebastian Bergmann and contributors.
..
Time: 0 seconds
OK (2 tests, 2 assertions)
~~~
上面這個調用例子中,PHPUnit 命令行測試執行器將在當前工作目錄中尋找 ArrayTest.php 源文件并加載之。而在此源文件中應當能找到 ArrayTest 測試用例類,此類中的測試將被執行。
對于每個測試的運行,PHPUnit 命令行工具輸出一個字符來指示進展:
.
當測試成功時輸出。
F
當測試方法運行過程中一個斷言失敗時輸出。
E
當測試方法運行過程中產生一個錯誤時輸出。
R
當測試被標記為有風險時輸出(參見[第 6 章](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#risky-tests))。
S
當測試被跳過時輸出(參見[第 7 章](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#incomplete-and-skipped-tests))。
I
當測試被標記為不完整或未實現時輸出(參見[第 7 章](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#incomplete-and-skipped-tests))。
PHPUnit 區分 敗(failure)與錯誤(error)。失敗指的是被違背了的 PHPUnit 斷言,例如一個失敗的 assertEquals() 調用。錯誤指的是意料之外的異常(exception)或 PHP 錯誤。這種差異已被證明在某些時候是非常有用的,因為錯誤往往比失敗更容易修復。如果得到了一個非常長的問題列表,那么最好先對付錯誤,當錯誤全部修復了之后再試一次瞧瞧還有沒有失敗。
#### 命令行選項
讓我們來瞧瞧以下代碼中命令行測試運行器的各種選項
~~~
phpunit --help
PHPUnit 4.6.0 by Sebastian Bergmann and contributors.
Usage: phpunit [options] UnitTest [UnitTest.php]
phpunit [options] <directory>
Code Coverage Options:
--coverage-clover <file> Generate code coverage report in Clover XML format.
--coverage-crap4j <file> Generate code coverage report in Crap4J XML format.
--coverage-html <dir> Generate code coverage report in HTML format.
--coverage-php <file> Export PHP_CodeCoverage object to file.
--coverage-text=<file> Generate code coverage report in text format.
Default: Standard output.
--coverage-xml <dir> Generate code coverage report in PHPUnit XML format.
Logging Options:
--log-junit <file> Log test execution in JUnit XML format to file.
--log-tap <file> Log test execution in TAP format to file.
--log-json <file> Log test execution in JSON format.
--testdox-html <file> Write agile documentation in HTML format to file.
--testdox-text <file> Write agile documentation in Text format to file.
Test Selection Options:
--filter <pattern> Filter which tests to run.
--testsuite <pattern> Filter which testsuite to run.
--group ... Only runs tests from the specified group(s).
--exclude-group ... Exclude tests from the specified group(s).
--list-groups List available test groups.
--test-suffix ... Only search for test in files with specified
suffix(es). Default: Test.php,.phpt
Test Execution Options:
--report-useless-tests Be strict about tests that do not test anything.
--strict-coverage Be strict about unintentionally covered code.
--strict-global-state Be strict about changes to global state
--disallow-test-output Be strict about output during tests.
--enforce-time-limit Enforce time limit based on test size.
--disallow-todo-tests Disallow @todo-annotated tests.
--process-isolation Run each test in a separate PHP process.
--no-globals-backup Do not backup and restore $GLOBALS for each test.
--static-backup Backup and restore static attributes for each test.
--colors Use colors in output.
--columns <n> Number of columns to use for progress output.
--columns max Use maximum number of columns for progress output.
--stderr Write to STDERR instead of STDOUT.
--stop-on-error Stop execution upon first error.
--stop-on-failure Stop execution upon first error or failure.
--stop-on-risky Stop execution upon first risky test.
--stop-on-skipped Stop execution upon first skipped test.
--stop-on-incomplete Stop execution upon first incomplete test.
-v|--verbose Output more verbose information.
--debug Display debugging information during test execution.
--loader <loader> TestSuiteLoader implementation to use.
--repeat <times> Runs the test(s) repeatedly.
--tap Report test execution progress in TAP format.
--testdox Report test execution progress in TestDox format.
--printer <printer> TestListener implementation to use.
Configuration Options:
--bootstrap <file> A "bootstrap" PHP file that is run before the tests.
-c|--configuration <file> Read configuration from XML file.
--no-configuration Ignore default configuration file (phpunit.xml).
--include-path <path(s)> Prepend PHP's include_path with given path(s).
-d key[=value] Sets a php.ini value.
Miscellaneous Options:
-h|--help Prints this usage information.
--version Prints the version and exits.
~~~
phpunit UnitTest
運行由 UnitTest 類提供的測試。這個類應當在 UnitTest.php 源文件中聲明。
UnitTest 這個類必須滿足以下二個條件之一:要么它繼承自 PHPUnit_Framework_TestCase;要么它提供 public static suite() 方法,這個方法返回一個 PHPUnit_Framework_Test 對象,比如,一個 PHPUnit_Framework_TestSuite 類的實例。
phpunit UnitTest UnitTest.php
運行由 UnitTest 類提供的測試。這個類應當在指定的源文件中聲明。
--coverage-clover
為運行的測試生成帶有代碼覆蓋率信息的 XML 格式的日志文件。更多細節請參見第 14 章。
請注意,此功能僅當安裝了 tokenizer 和 Xdebug 這兩個 PHP 擴展后才可用。
--coverage-crap4j
生成 Crap4j 格式的代碼覆蓋率報告。更多細節請參見第 11 章。
請注意,此功能僅當安裝了 tokenizer 和 Xdebug 這兩個 PHP 擴展后才可用。
--coverage-html
生成 HTML 格式的代碼覆蓋率報告。更多細節請參見 第 11 章。
請注意,此功能僅當安裝了 tokenizer 和 Xdebug 這兩個 PHP 擴展后才可用。
--coverage-php
生成一個序列化后的 PHP_CodeCoverage 對象,此對象含有代碼覆蓋率信息。
請注意,此功能僅當安裝了 tokenizer 和 Xdebug 這兩個 PHP 擴展后才可用。
--coverage-text
為運行的測試以人們可讀的格式生成帶有代碼覆蓋率信息的日志文件或命令行輸出。更多細節請參見 第 14 章。
請注意,此功能僅當安裝了 tokenizer 和 Xdebug 這兩個 PHP 擴展后才可用。
--log-junit
為運行的測試生成 JUnit XML 格式的日志文件。更多細節請參見 第 14 章。
--log-tap
為運行的測試生成 Test Anything Protocol (TAP) 格式的日志文件。更多細節請參見第 14 章。
--log-json
生成 JSON 格式的日志文件。更多細節請參見第 14 章。
--testdox-html 和 --testdox-text
為運行的測試以 HTML 或純文本格式生成敏捷文檔。更多細節請參見 第 12 章。
--filter
只運行名稱與給定模式匹配的測試。如果模式未閉合包裹于分隔符,PHPUnit 將用 / 分隔符對其進行閉合包裹。
測試名稱將以以下格式之一進行匹配:
TestNamespace\TestCaseClass::testMethod
-testsuite
只運行名稱與給定模式匹配的測試套件。
--group
只運行來自指定分組(可以多個)的測試。可以用 @group 標注為測試標記其所屬的分組。
@author 標注是 @group 的一個別名,允許按作者來篩選測試。
--exclude-group
排除來自指定分組(可以多個)的測試。可以用 @group 標注為測試標記其所屬的分組。
--list-groups
列出所有有效的測試分組。
--test-suffix
只查找文件名以指定后綴(可以多個)結尾的測試文件。
--report-useless-tests
更嚴格對待事實上不測試任何內容的測試。詳情參見 第 6 章。
--strict-coverage
更嚴格對待意外的代碼覆蓋。詳情參見 第 6 章。
--strict-global-state
更嚴格對待全局狀態篡改。詳情參見 第 6 章。
--disallow-test-output
更嚴格對待測試執行期間產生的輸出。詳情參見第 6 章。
--disallow-todo-tests
不執行文檔注釋塊中含有 @todo 標注的測試。
--enforce-time-limit
根據測試規模對其加上執行時長限制。詳情參見第 6 章。
--strict
以嚴格模式運行測試(效果的功能等同于同時使用 --report-useless-tests、--strict-coverage、--disallow-test-output 和 --enforce-time-limit)。詳情參見第 6 章。
--process-isolation
每個測試都在獨立的PHP進程中運行。
--no-globals-backup
不要備份并還原 $GLOBALS。更多細節請參見“全局狀態”一節。
--static-backup
備份并還原用戶定義的類中的靜態屬性。更多細節請參見“全局狀態”一節。
--colors
使用彩色輸出。Windows下,用 ANSICON 或 ConEmu。
--stderr
選擇輸出到 STDERR 而非 STDOUT.
--stop-on-error
首次錯誤出現后停止執行。
--stop-on-failure
首次錯誤或失敗出現后停止執行。
--stop-on-risky
首次碰到有風險的測試時停止執行。
--stop-on-skipped
首次碰到跳過的測試時停止執行。
--stop-on-incomplete
首次碰到不完整的測試時停止執行。
--verbose
輸出更詳盡的信息,例如不完整或者跳過的測試的名稱。
--debug
輸出調試信息,例如當一個測試開始執行時輸出其名稱。
--loader
指定要使用的 PHPUnit_Runner_TestSuiteLoader 實現。
標準的測試套件加載器將在當前工作目錄和 PHP 的 include_path 配置指令中指定的每個目錄內查找源文件。諸如 Project_Package_Class 這樣的類名對應的源文件名為 Project/Package/Class.php。
--repeat
將測試重復運行指定次數。
--tap
使用 Test Anything Protocol (TAP) 報告測試進度。更多細節請參見 第 14 章。
--testdox
將測試進度以敏捷文檔方式報告。更多細節請參見 第 12 章。
--printer
指定要使用的結果輸出器(printer)。輸出器類必須擴展 PHPUnit_Util_Printer 并且實現 PHPUnit_Framework_TestListener 接口。
--bootstrap
在測試前先運行一個 "bootstrap" PHP 文件。
--configuration, -c
從 XML 文件中讀取配置信息。更多細節請參見附錄 C。
如果 phpunit.xml 或 phpunit.xml.dist (按此順序)存在于當前工作目錄并且未使用 --configuration,將自動從此文件中讀取配置。
--no-configuration
忽略當前工作目錄下的 phpunit.xml 與 phpunit.xml.dist。
--include-path
向 PHP 的 include_path 開頭添加指定路徑(可以多個)。
-d
設置指定的 PHP 配置選項的值。
**注意**
請注意,選項不能放在參數之后。
### 基境
在編寫測試時,最費時的部分之一是編寫代碼來將整個場景設置成某個已知的狀態,并在測試結束后將其復原到初始狀態。這個已知的狀態稱為測試的 基境(fixture)。
#### 可用方法
`setUp()` 和 `tearDown()` 模板方法(同時,每個測試方法都是在一個全新的測試類實例上運行的)。
另外,`setUpBeforeClass()` 與 `tearDownAfterClass()` 模板方法將分別在測試用例類的第一個測試運行之前和測試用例類的最后一個測試運行之后調用。
### 全局變量
[使用單件(singleton)的代碼很難測試](http://googletesting.blogspot.com/2008/05/tott-using-dependancy-injection-to.html)。使用全局變量的代碼也一樣。通常情況下,欲測代碼和全局變量之間會強烈耦合,并且其創建無法控制。另外一個問題是,一個測試對全局變量的改變可能會破壞另外一個測試。
默認情況下,PHPUnit 用一種更改全局變量與超全局變量($GLOBALS、$_ENV、$_POST、$_GET、$_COOKIE、$_SERVER、$_FILES、$_REQUEST)不會影響到其他測試的方式來運行所有測試。同時,還可以選擇將這種隔離擴展到類的靜態屬性。
默認情況下 測試方法間全局變量是失效的。
~~~
<?php
class StackTest extends PHPUnit_Framework_TestCase{
public function testOne{
$_SESSION['a'] = 'b';
}
public function testTwo(){
var_dump(isset($_SESSION['a'])); //結果是false
}
}
?>
~~~
解決的方法是,在繼承PHPUnit_Framework_TestCase 的測試類里 設置屬性
`protected $backupGlobals = FALSE;`
### 測試順序控制
#### 文件系統順序
>當 PHPUnit 命令行測試執行器指向一個目錄時,它會在目錄下查找 *Test.php 文件。
最簡單的大概就是把所有測試用例源文件放在一個測試目錄中。通過對測試目錄進行遞歸遍歷,PHPUnit 能自動發現并運行測試。
現在來看看 sebastianbergmann/money 這個庫的測試套件。在這個項目的目錄結構中,可以看到 tests 目錄下的測試用例類鏡像了 src 目錄下被測系統(SUT, System Under Test)的包(package)與類(class)的結構:
~~~
src tests
`-- Currency.php `-- CurrencyTest.php
`-- IntlFormatter.php `-- IntlFormatterTest.php
`-- Money.php `-- MoneyTest.php
`-- autoload.php
~~~
要運行這個庫的全部測試,只要將 PHPUnit 命令行測試執行器指向測試目錄即可:
~~~
phpunit --bootstrap src/autoload.php tests
PHPUnit 4.6.0 by Sebastian Bergmann.
.................................
Time: 636 ms, Memory: 3.50Mb
OK (33 tests, 52 assertions)
~~~
#### XML文件配置
PHPUnit的 XML 配置文件([附錄 C](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#appendixes.configuration))也可以用于編排測試套件。例 5.1展示了一個最小化的 phpunit.xml 例子,它將在遞歸遍歷 tests 時添加所有在 *Test.php 文件中找到的 *Test 類。
例 5.1: 用 XML 配置來編排測試套件
~~~
<phpunit bootstrap="src/autoload.php">
<testsuites>
<testsuite name="money">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>
~~~
如果 phpunit.xml 或 phpunit.xml.dist (按此順序)存在于當前工作目錄并且未使用 --configuration,將自動從此文件中讀取配置。
可以明確指定測試的執行順序:
例 5.2: 用 XML 配置來編排測試套件
~~~
<phpunit bootstrap="src/autoload.php">
<testsuites>
<testsuite name="money">
<file>tests/IntlFormatterTest.php</file>
<file>tests/MoneyTest.php</file>
<file>tests/CurrencyTest.php</file>
</testsuite>
</testsuites>
</phpunit>
~~~
### 跳過測試
并非所有測試都能在任何環境中運行。比如說,考慮這樣一種情況:一個數據庫抽象層,針對其所支持的各種數據庫系統有多個不同的驅動程序。針對 MySQL 驅動程序的測試當然只在 MySQL 服務器可用才能運行。
例 7.2 展示了一個測試用例類 DatabaseTest,它有一個測試方法 testConnection()。在測試用例類的 setUp()模板方法中,檢查了 MySQLi 擴展是否可用,并且在擴展不可用時用 markTestSkipped() 方法來跳過此測試。
例 7.2: 跳過某個測試
~~~
<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!extension_loaded('mysqli')) {
$this->markTestSkipped(
'The MySQLi 擴展不可用。'
);
}
}
public function testConnection()
{
// ...
}
}
?>
~~~
在 PHPUnit 命令行測試執行器的輸出中,被跳過的測試記為 S,如下例所示:
~~~
phpunit --verbose DatabaseTest
PHPUnit 4.6.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 來跳過測試
除了上述方法,還可以用 @requires 標注來表達測試用例的一些常見前提條件。
表 7.3. 可能的 @requires 用法
|類型 |可能的值 |范例 |其他范例|
|-----|-----|------|------|
|PHP |任何 PHP 版本標識符 |@requires PHP 5.3.3 |@requires PHP 5.4-dev|
|PHPUnit |任何 PHPUnit 版本標識符 |@requires PHPUnit 3.6.3 |@requires PHPUnit 4.6|
|OS |用來對 PHP_OS 進行匹配的正則表達式 |@requires OS Linux |@requires OS WIN32 WINNT|
|function |任何對 function_exists 而言有效的參數 |@requires function imap_open |@requires function ReflectionMethod::setAccessible|
|extension |任何擴展的名稱 |@requires extension| mysqli @requires extension curl|
例 7.3: 用 @requires 來跳過測試
~~~
<?php
/**
* @requires extension mysqli
*/
class DatabaseTest extends PHPUnit_Framework_TestCase
{
/**
* @requires PHP 5.3
*/
public function testConnection()
{
// 測試要求有 mysqli 擴展,并且 PHP >= 5.3
}
// ... 所有其他要求有 mysqli 擴展的測試
}
?>
~~~
如果使用了某種在特定版本的 PHP 下無法編譯的語法,請在此章節內查找 XML 配置信息中關于版本依賴的信息:[“測試套件”一節](https://phpunit.de/manual/current/zh_cn/phpunit-book.html#appendixes.configuration.testsuites)
# 個人思考
## 測試
先有測試后開發,測試能提高質量
自動化 可以快速定義錯誤
測試原則:
1. 最大覆蓋測試
2. 測試有依賴可以快速定位
3. 一致性,不要為了命名這件事浪費時間
## PHPUnit
### 測試得出代碼寫的技巧
1. 數據庫操作級的函數和方法 寫在前面
2. 易被別處調用的基礎方法寫上面
3. 每個函數或方法最好有返回值,特殊情況例外
## 一個ThinkPHP單元測試的例子:
大家可以看隨書項目的示列項目 [yang_book](https://coding.net/u/jaylabs/p/yang_book/git) 里phpunit里tests目錄下的測試,就寫了個函數測試。
因為發現tp耦合性太高,文件加載自動沒法弄,除非手寫死所有框架要加載的文件要死人的。
所以就只自己寫了加載框架和函數及慣例配置文件。函數庫里一些和類相關及文件加載相關的函數也沒測試。
因為涉及類的命名空間,正常來說都能加載、實例化成功。就沒測試。

先命令行切換到單元測試目錄。然后 phpunit 指定目錄即可。

也可以手動指定一個測試方法。
PS:老大說Thinkphp4會有單元測試的。
其實很多tp用戶也關心測試這個問題,寫了一些框架來解決單元測試問題:
- [一個為ThinkPHP打造的簡單易用的UnitTest](http://www.thinkphp.cn/extend/670.html)
- [ThinkPHP3.2單元測試實踐](http://www.thinkphp.cn/topic/31450.html)
- [LTPTest2.0.0 -- TP 下的單元測試框架擴展](http://www.thinkphp.cn/extend/599.html)
- 序
- 前言
- 內容簡介
- 目錄
- 基礎知識
- 起步
- 控制器
- 模型
- 模板
- 命名空間
- 進階知識
- 路由
- 配置
- 緩存
- 權限
- 擴展
- 國際化
- 安全
- 單元測試
- 拿來主義
- 調試方法
- 調試的步驟
- 調試工具
- 顯示trace信息
- 開啟調試和關閉調試的區別
- netbeans+xdebug
- Socketlog
- PHP常見錯誤
- 小黃鴨調試法,每個程序員都要知道的
- 應用場景
- 第三方登錄
- 圖片處理
- 博客
- SAE
- REST實踐
- Cli
- ajax分頁
- barcode條形碼
- excel
- 發郵件
- 漢字轉全拼和首字母,支持帶聲調
- 中文分詞
- 瀏覽器useragent解析
- freelog項目實戰
- 需求分析
- 數據庫設計
- 編碼實踐
- 前端實現
- rest接口
- 文章發布
- 文件上傳
- 視頻播放
- 音樂播放
- 圖片幻燈片展示
- 注冊和登錄
- 個人資料更新
- 第三方登錄的使用
- 后臺
- 微信的開發
- 首頁及個人主頁
- 列表
- 歸檔
- 搜索
- 分頁
- 總結經驗
- 自我提升
- 進行小項目的鍛煉
- 對現有輪子的重構和移植
- 寫技術博客
- 制作視頻教程
- 學習PHP的知識和新特性
- 和同行直接溝通、交流
- 學好英語,走向國際
- 如何參與
- 瀏覽官網和極思維還有看云
- 回答ThinkPHP新手的問題
- 嘗試發現ThinkPHP的bug,告訴官方人員或者push request
- 開發能提高效率的ThinkPHP工具
- 嘗試翻譯官方文檔
- 幫新手入門
- 創造基于ThinkPHP的產品,進行連帶推廣
- 展望未來
- OneThink
- ThinkPHP4
- 附錄