# 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. 版权