> 没有经过测试的代码不是完成的代码。
对于敏捷开发来说,测试也是重要的一环。对于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耦合性太高,文件加载自动没法弄,除非手写死所有框架要加载的文件要死人的。
所以就只自己写了加载框架和函数及惯例配置文件。函数库里一些和类相关及文件加载相关的函数也没测试。
因为涉及类的命名空间,正常来说都能加载、实例化成功。就没测试。
![document/2015-08-23/55d9c1b5746ea](http://box.kancloud.cn/document_2015-08-23_55d9c1b5746ea.png)
先命令行切换到单元测试目录。然后 phpunit 指定目录即可。
![document/2015-08-23/55d9c1e94f64b](http://box.kancloud.cn/document_2015-08-23_55d9c1e94f64b.png)
也可以手动指定一个测试方法。
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
- 附录