# PHP 檔案包 (PHAR)
要獲取 PHPUnit,最簡單的方法是下載 PHPUnit 的 [PHP 檔案包 (PHAR)](http://php.net/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](http://suhosin.org/) 擴展,需要在 `php.ini` 中允許執行 PHAR:
~~~
suhosin.executor.include.whitelist = phar
~~~
### Note
要從 `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](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`** 將其設為可執行。
### 校驗 PHPUnit PHAR 發行包
由 PHPUnit 項目分發的所有官方代碼發行包都由發行包管理器進行簽名。在 [phar.phpunit.de](https://phar.phpunit.de/) 上有 PGP 簽名和 SHA1 散列值可用于校驗。
下面的例子詳細說明了如何對發行包進行校驗。首先下載 `phpunit.phar` 和與之對應的單獨 PGP 簽名 `phpunit.phar.asc`:
~~~
wget https://phar.phpunit.de/phpunit.phar
wget https://phar.phpunit.de/phpunit.phar.asc
~~~
用單獨的簽名(`phpunit.phar`)對 PHPUnit 的 PHP 檔案包(`phpunit.phar.asc`)進行校驗:
~~~
gpg phpunit.phar.asc
gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A
gpg: Can't check signature: public key not found
~~~
在本地系統中沒有發行包管理器的公鑰(`6372C20A`)。為了能進行校驗,必須從某個密鑰服務器上取得發行包管理器的公鑰。其中一個服務器是 `pgp.uni-mainz.de`。所有密鑰服務器是鏈接在一起的,因此連接到任一密鑰服務器都可以。
~~~
gpg --keyserver pgp.uni-mainz.de --recv-keys 0x4AA394086372C20A
gpg: requesting key 6372C20A from hkp server pgp.uni-mainz.de
gpg: key 6372C20A: public key "Sebastian Bergmann <sb@sebastian-bergmann.de>" imported
gpg: Total number processed: 1
gpg: imported: 1 (RSA: 1)
~~~
現在已經取得了條目名稱為"Sebastian Bergmann <sb@sebastian-bergmann.de>"的公鑰。不過無法檢驗這個密鑰確實是由名叫 Sebastian Bergmann 的人創建的。但是可以先試著校驗發行包的簽名:
~~~
gpg phpunit.phar.asc
gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A
gpg: Good signature from "Sebastian Bergmann <sb@sebastian-bergmann.de>"
gpg: aka "Sebastian Bergmann <sebastian@php.net>"
gpg: aka "Sebastian Bergmann <sebastian@thephp.cc>"
gpg: aka "Sebastian Bergmann <sebastian@phpunit.de>"
gpg: aka "Sebastian Bergmann <sebastian.bergmann@thephp.cc>"
gpg: aka "[jpeg image of size 40635]"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A
~~~
此時,簽名已經沒問題了,但是這個公鑰還不能信任。簽名沒問題意味著文件未被篡改。可是由于公鑰加密系統的性質,還需要再校驗密鑰 `6372C20A` 確實是由真正的 Sebastian Bergmann 創建的。
任何攻擊者都能創建公鑰并將其上傳到公鑰服務器。他們可以建立一個帶惡意的發行包,并用這個假密鑰進行簽名。這樣,如果嘗試對這個損壞了的發行包進行簽名校驗,由于密鑰是“真”密鑰,校驗將成功完成。因此,需要對這個密鑰的真實性進行校驗。如何對公鑰的真實性進行校驗已經超出了本文檔的范疇。
有個比較謹慎的做法是創建一個腳本來管理 PHPUnit 的安裝,在運行測試套件之前校驗 GnuPG 簽名。例如:
~~~
#!/usr/bin/env bash
clean=1 # 是否在測試完成之后刪除 phpunit.phar ?
aftercmd="php phpunit.phar --bootstrap bootstrap.php src/tests"
gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
if [ $? -ne 0 ]; then
echo -e "\033[33mDownloading PGP Public Key...\033[0m"
gpg --recv-keys D8406D0D82947747293778314AA394086372C20A
# Sebastian Bergmann <sb@sebastian-bergmann.de>
gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
if [ $? -ne 0 ]; then
echo -e "\033[31mCould not download PGP public key for verification\033[0m"
exit
fi
fi
if [ "$clean" -eq 1 ]; then
# 如果存在就清理掉
if [ -f phpunit.phar ]; then
rm -f phpunit.phar
fi
if [ -f phpunit.phar.asc ]; then
rm -f phpunit.phar.asc
fi
fi
# 抓取最新的發行版和對應的簽名
if [ ! -f phpunit.phar ]; then
wget https://phar.phpunit.de/phpunit.phar
fi
if [ ! -f phpunit.phar.asc ]; then
wget https://phar.phpunit.de/phpunit.phar.asc
fi
# 在運行前先校驗
gpg --verify phpunit.phar.asc phpunit.phar
if [ $? -eq 0 ]; then
echo
echo -e "\033[33mBegin Unit Testing\033[0m"
# 運行測試套件
`$after_cmd`
# 清理
if [ "$clean" -eq 1 ]; then
echo -e "\033[32mCleaning Up!\033[0m"
rm -f phpunit.phar
rm -f phpunit.phar.asc
fi
else
echo
chmod -x phpunit.phar
mv phpunit.phar /tmp/bad-phpunit.phar
mv phpunit.phar.asc /tmp/bad-phpunit.phar.asc
echo -e "\033[31mSignature did not match! PHPUnit has been moved to /tmp/bad-phpunit.phar\033[0m"
exit 1
fi
~~~
- PHPUnit 手冊
- 1. 安裝 PHPUnit
- 需求
- PHP 檔案包 (PHAR)
- Composer
- 可選的組件包
- 2. 編寫 PHPUnit 測試
- 測試的依賴關系
- 數據供給器
- 對異常進行測試
- 對 PHP 錯誤進行測試
- 對輸出進行測試
- 錯誤相關信息的輸出
- 3. 命令行測試執行器
- 命令行選項
- 4. 基境(fixture)
- setUp() 多 tearDown() 少
- 變體
- 基境共享
- 全局狀態
- 5. 組織測試
- 用文件系統來編排測試套件
- 用 XML 配置來編排測試套件
- 6. 有風險的測試
- 無用測試
- 意外的代碼覆蓋
- 測試執行期間產生的輸出
- 測試執行時長的超時限制
- 全局狀態篡改
- 7. 未完成的測試與跳過的測試
- 未完成的測試
- 跳過測試
- 用 @requires 來跳過測試
- 8. 數據庫測試
- 數據庫測試所支持的供應商
- 數據庫測試的難點
- 數據庫測試的四個階段
- PHPUnit 數據庫測試用例的配置
- 理解 DataSet(數據集)和 DataTable(數據表)
- 數據庫連接 API
- 數據庫斷言 API
- 常見問題(FAQ)
- 9. 測試替身
- Stubs (樁件)
- 仿件對象(Mock Object)
- Prophecy
- 對特質(Trait)與抽象類進行模仿
- 對 Web 服務(Web Services)進行上樁或模仿
- 對文件系統進行模仿
- 10. 測試實踐
- 在開發過程中
- 在調試過程中
- 11. 代碼覆蓋率分析
- 用于代碼覆蓋率的軟件衡量標準
- 包含與排除文件
- 略過代碼塊
- 指明要覆蓋的方法
- 邊緣情況
- 12. 測試的其他用途
- 敏捷文檔
- 跨團隊測試
- 13. Logging (日志記錄)
- 測試結果 (XML)
- 測試結果 (TAP)
- 測試結果 (JSON)
- 代碼覆蓋率 (XML)
- 代碼覆蓋率 (TEXT)
- 14. 擴展 PHPUnit
- 從 PHPUnit_Framework_TestCase 派生子類
- 編寫自定義斷言
- 實現 PHPUnit_Framework_TestListener
- 從 PHPUnit_Extensions_TestDecorator 派生子類
- 實現 PHPUnit_Framework_Test
- A. 斷言
- assertArrayHasKey()
- assertClassHasAttribute()
- assertArraySubset()
- assertClassHasStaticAttribute()
- assertContains()
- assertContainsOnly()
- assertContainsOnlyInstancesOf()
- assertCount()
- assertEmpty()
- assertEqualXMLStructure()
- assertEquals()
- assertFalse()
- assertFileEquals()
- assertFileExists()
- assertGreaterThan()
- assertGreaterThanOrEqual()
- assertInfinite()
- assertInstanceOf()
- assertInternalType()
- assertJsonFileEqualsJsonFile()
- assertJsonStringEqualsJsonFile()
- assertJsonStringEqualsJsonString()
- assertLessThan()
- assertLessThanOrEqual()
- assertNan()
- assertNull()
- assertObjectHasAttribute()
- assertRegExp()
- assertStringMatchesFormat()
- assertStringMatchesFormatFile()
- assertSame()
- assertStringEndsWith()
- assertStringEqualsFile()
- assertStringStartsWith()
- assertThat()
- assertTrue()
- assertXmlFileEqualsXmlFile()
- assertXmlStringEqualsXmlFile()
- assertXmlStringEqualsXmlString()
- B. 標注
- @author
- @after
- @afterClass
- @backupGlobals
- @backupStaticAttributes
- @before
- @beforeClass
- @codeCoverageIgnore*
- @covers
- @coversDefaultClass
- @coversNothing
- @dataProvider
- @depends
- @expectedException
- @expectedExceptionCode
- @expectedExceptionMessage
- @expectedExceptionMessageRegExp
- @group
- @large
- @medium
- @preserveGlobalState
- @requires
- @runTestsInSeparateProcesses
- @runInSeparateProcess
- @small
- @test
- @testdox
- @ticket
- @uses
- C. XML 配置文件
- PHPUnit
- 測試套件
- 分組
- 為代碼覆蓋率包含或排除文件
- Logging (日志記錄)
- 測試監聽器
- 設定 PHP INI 設置、常量、全局變量
- 為 Selenium RC 配置瀏覽器
- D. 升級
- E. 索引
- F. 參考書目
- G. 版權