12. 安装 PHPUnit
2
C:Usersusername> cd C:bin
C:bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
C:bin> exit
6. 新开一个命令行窗口,确认一下可以在任意路径下执行 PHPUnit:
C:Usersusername> 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 创建的。
13. 安装 PHPUnit
3
任何攻击者都能创建公钥并将其上传到公钥服务器。他们可以建立一个带恶意的发行包,并
用这个假密钥进行签名。这样,如果尝试对这个损坏了的发行包进行签名校验,由于密钥
是“真”密钥,校验将成功完成。因此,需要对这个密钥的真实性进行校验。如何对公钥的
真实性进行校验已经超出了本文档的范畴。
有个比较谨慎的做法是创建一个脚本来管理 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 verification033[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 Testing033[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.
exit 1
fi
24. 编写 PHPUnit 测试
14
例 2.12. 用 @expectedException 来预期 PHP 错误
<?php
use PHPUnitFrameworkTestCase;
class ExpectedErrorTest extends TestCase
{
/**
* @expectedException PHPUnitFrameworkError
*/
public function testFailingInclude()
{
include 'not_existing_file.php';
}
}
?>
phpunit -d error_reporting=2 ExpectedErrorTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
.
Time: 0 seconds, Memory: 5.25Mb
OK (1 test, 1 assertion)
PHPUnitFrameworkErrorNotice 和 PHPUnitFrameworkErrorWarning 分
别代表 PHP 通知与 PHP 警告。
注意
对异常进行测试是越明确越好的。对太笼统的类进行测试有可能导致不良副作用。
因此,不再允许用 @expectedException 或 setExpectedException() 对
Exception 类进行测试。
如果测试依靠会触发错误的 PHP 函数,例如 fopen ,有时候在测试中使用错误抑制符会很
有用。通过抑制住错误通知,就能对返回值进行检查,否则错误通知将会导致抛出
PHPUnitFrameworkErrorNotice。
例 2.13. 对会引发PHP 错误的代码的返回值进行测试
<?php
use PHPUnitFrameworkTestCase;
class ErrorSuppressionTest extends TestCase
{
public function testFileWriting() {
$writer = new FileWriter;
$this->assertFalse(@$writer->write('/is-not-writeable/file', 'stuff'));
}
}
class FileWriter
{
public function write($file, $content) {
$file = fopen($file, 'w');
if($file == false) {
return false;
}
// ...
}
}
25. 编写 PHPUnit 测试
15
?>
phpunit ErrorSuppressionTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
.
Time: 1 seconds, Memory: 5.25Mb
OK (1 test, 1 assertion)
如果不使用错误抑制符,此测试将会失败,并报告 fopen(/is-not-writeable/file):
failed to open stream: No such file or directory。
对输出进行测试
有时候,想要断言(比如说)某方法的运行过程中生成了预期的输出(例如,通过 echo 或
print)。PHPUnitFrameworkTestCase 类使用 PHP 的 输出缓冲 [http://www.php.net/
manual/en/ref.outcontrol.php] 特性来为此提供必要的功能支持。
例 2.14 “对函数或方法的输出进行测试”展示了如何用 expectOutputString() 方法来
设定所预期的输出。如果没有产生预期的输出,测试将计为失败。
例 2.14. 对函数或方法的输出进行测试
<?php
use PHPUnitFrameworkTestCase;
class OutputTest extends TestCase
{
public function testExpectFooActualFoo()
{
$this->expectOutputString('foo');
print 'foo';
}
public function testExpectBarActualBaz()
{
$this->expectOutputString('bar');
print 'baz';
}
}
?>
phpunit OutputTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
.F
Time: 0 seconds, Memory: 5.75Mb
There was 1 failure:
1) OutputTest::testExpectBarActualBaz
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
26. 编写 PHPUnit 测试
16
+'baz'
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
表 2.1 “用于对输出进行测试的方法”中列举了用于对输出进行测试的各种方法。
表 2.1. 用于对输出进行测试的方法
方法 含义
void expectOutputRegex(string
$regularExpression)
设置输出预期为输出应当匹配正则表达式
$regularExpression。
void expectOutputString(string
$expectedString)
设置输出预期为输出应当与
$expectedString 字符串相等。
bool setOutputCallback(callable
$callback)
设置回调函数,用来做诸如将实际输出规范
化之类的动作。
string getActualOutput() 获取实际输出。
注意
在严格模式下,本身产生输出的测试将会失败。
错误相关信息的输出
当有测试失败时,PHPUnit 全力提供尽可能多的有助于找出问题所在的上下文信息。
例 2.15. 数组比较失败时生成的错误相关信息输出
<?php
use PHPUnitFrameworkTestCase;
class ArrayDiffTest extends TestCase
{
public function testEquality() {
$this->assertEquals(
[1, 2, 3, 4, 5, 6],
[1, 2, 33, 4, 5, 6]
);
}
}
?>
phpunit ArrayDiffTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) ArrayDiffTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
28. 编写 PHPUnit 测试
18
边缘情况
当比较失败时,PHPUnit 为输入值建立文本表示,然后以此进行对比。这种实现导致在差异
指示中显示出来的问题可能比实际上存在的多。
这种情况只出现在对数组或者对象使用 assertEquals 或其他“弱”比较函数时。
例 2.17. 当使用弱比较时在生成的差异结果中出现的边缘情况
<?php
use PHPUnitFrameworkTestCase;
class ArrayWeakComparisonTest extends TestCase
{
public function testEquality() {
$this->assertEquals(
[1, 2, 3, 4, 5, 6],
['1', 2, 33, 4, 5, 6]
);
}
}
?>
phpunit ArrayWeakComparisonTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) ArrayWeakComparisonTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
- 0 => 1
+ 0 => '1'
1 => 2
- 2 => 3
+ 2 => 33
3 => 4
4 => 5
5 => 6
)
/home/sb/ArrayWeakComparisonTest.php:7
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
在这个例子中,第一个索引项中的 1 and '1' 在报告中被视为不同,虽然 assertEquals 认为这
两个值是匹配的。
29. 19
第 3 章 命令行测试执行器
PHPUnit 命令行测试执行器可通过 phpunit 命令调用。下面的代码展示了如何用 PHPUnit 命
令行测试执行器来运行测试:
phpunit ArrayTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
..
Time: 0 seconds
OK (2 tests, 2 assertions)
上面这个调用例子中,PHPUnit 命令行测试执行器将在当前工作目录中寻找
ArrayTest.php 源文件并加载之。而在此源文件中应当能找到 ArrayTest 测试用例类,
此类中的测试将被执行。
对于每个测试的运行,PHPUnit 命令行工具输出一个字符来指示进展:
. 当测试成功时输出。
F 当测试方法运行过程中一个断言失败时输出。
E 当测试方法运行过程中产生一个错误时输出。
R 当测试被标记为有风险时输出(参见第 6 章 有风险的测试)。
S 当测试被跳过时输出(参见第 7 章 未完成的测试与跳过的测试)。
I 当测试被标记为不完整或未实现时输出(参见第 7 章 未完成的测试与跳过的测试)。
PHPUnit 区分 败(failure)与错误(error)。失败指的是被违背了的 PHPUnit 断
言,例如一个失败的 assertEquals() 调用。错误指的是意料之外的异常(exception)或
PHP 错误。这种差异已被证明在某些时候是非常有用的,因为错误往往比失败更容易修复。
如果得到了一个非常长的问题列表,那么最好先对付错误,当错误全部修复了之后再试一次
瞧瞧还有没有失败。
命令行选项
让我们来瞧瞧以下代码中命令行测试运行器的各种选项:
phpunit --help
PHPUnit 6.5.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.
--whitelist <dir> Whitelist <dir> for code coverage analysis.
--disable-coverage-ignore Disable annotations for ignoring code coverage.
Logging Options:
30. 命令行测试执行器
20
--log-junit <file> Log test execution in JUnit XML format to file.
--log-teamcity <file> Log test execution in TeamCity format to file.
--testdox-html <file> Write agile documentation in HTML format to file.
--testdox-text <file> Write agile documentation in Text format to file.
--testdox-xml <file> Write agile documentation in XML format to file.
--reverse-list Print defects in reverse order
Test Selection Options:
--filter <pattern> Filter which tests to run.
--testsuite <name,...> 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.
--list-suites List available test suites.
--test-suffix ... Only search for test in files with specified
suffix(es). Default: Test.php,.phpt
Test Execution Options:
--dont-report-useless-tests Do not report tests that do not test anything.
--strict-coverage Be strict about @covers annotation usage.
--strict-global-state Be strict about changes to global state
--disallow-test-output Be strict about output during tests.
--disallow-resource-usage Be strict about resource usage during small 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.
--globals-backup Backup and restore $GLOBALS for each test.
--static-backup Backup and restore static attributes for each test.
--colors=<flag> Use colors in output ("never", "auto" or "always").
--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-warning Stop execution upon first warning.
--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.
--fail-on-warning Treat tests with warnings as failures.
--fail-on-risky Treat risky tests as failures.
-v|--verbose Output more verbose information.
--debug Display debugging information.
--loader <loader> TestSuiteLoader implementation to use.
--repeat <times> Runs the test(s) repeatedly.
--teamcity Report test execution progress in TeamCity format.
--testdox Report test execution progress in TestDox format.
--testdox-group Only include tests from the specified group(s).
--testdox-exclude-group Exclude tests from the specified group(s).
--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).
--no-coverage Ignore code coverage configuration.
--no-extensions Do not load PHPUnit extensions.
--include-path <path(s)> Prepend PHP's include_path with given path(s).
31. 命令行测试执行器
21
-d key[=value] Sets a php.ini value.
--generate-configuration Generate configuration file with suggested settings.
Miscellaneous Options:
-h|--help Prints this usage information.
--version Prints the version and exits.
--atleast-version <min> Checks that version is greater than min and exits.
phpunit UnitTest 运行由 UnitTest 类提供的测试。这个类应当在
UnitTest.php 源文件中声明。
UnitTest 这个类必须满足以下二个条件之一:要么它
继承自 PHPUnitFrameworkTestCase;要么它提供
public static suite() 方法,这个方法返回
一个 PHPUnit_Framework_Test 对象,比如,一个
PHPUnit_Framework_TestSuite 类的实例。
phpunit UnitTest
UnitTest.php
运行由 UnitTest 类提供的测试。这个类应当在指定的
源文件中声明。
--coverage-clover 为运行的测试生成带有代码覆盖率信息的 XML 格式的
日志文件。更多细节请参见第 13 章 Logging (日志记
录)。
请注意,此功能仅当安装了 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 为运行的测试以人们可读的格式生成带有代码覆盖率信
息的日志文件或命令行输出。更多细节请参见 第 13 章
Logging (日志记录)。
请注意,此功能仅当安装了 tokenizer 和 Xdebug 这两个
PHP 扩展后才可用。
--log-junit 为运行的测试生成 JUnit XML 格式的日志文件。更多细节
请参见 第 13 章 Logging (日志记录)。
--testdox-html 和 --
testdox-text
为运行的测试以 HTML 或纯文本格式生成敏捷文档。更
多细节请参见 第 12 章 测试的其他用途。
--filter 只运行名称与给定模式匹配的测试。如果模式未闭合包
裹于分隔符,PHPUnit 将用 / 分隔符对其进行闭合包裹。
测试名称将以以下格式之一进行匹配:
32. 命令行测试执行器
22
TestNamespace
TestCaseClass::testMethod
默认的测试名称格式
等价于在测试方法内
使用 __METHOD__ 魔
术常量。
TestNamespace
TestCaseClass::testMethod
with data set #0
当测试拥有数据供给
器时,数据的每轮迭
代都会将其当前索引
附加在默认测试名称
结尾处。
TestNamespace
TestCaseClass::testMethod
with data set "my named
data"
当测试拥有使用命名
数据集的数据供给器
时,数据的每轮迭代
都会将当前名称附加
在默认测试名称结尾
处。命名数据集的例
子参见例 3.1 “命名
数据集”。
例 3.1. 命名数据集
<?php
use PHPUnitFrameworkTestCase;
namespace TestNamespace;
class TestCaseClass extends Tes
{
/**
* @dataProvider provider
*/
public function testMethod(
{
$this->assertTrue($data
}
public function provider()
{
return [
'my named data' =>
'my data' =>
];
}
}
?>
/path/to/my/test.phpt 对于 PHPT 测试,其
测试名称是文件系统
路径。
有效的过滤器模式例子参见例 3.2 “过滤器模式例子”。
例 3.2. 过滤器模式例子
• --filter 'TestNamespace
TestCaseClass::testMethod'
• --filter 'TestNamespaceTestCaseClass'
58. 数据库测试
48
/**
* @param array $data
*/
public function __construct(array $data)
{
foreach ($data AS $tableName => $rows) {
$columns = [];
if (isset($rows[0])) {
$columns = array_keys($rows[0]);
}
$metaData = new PHPUnit_Extensions_Database_DataSet_DefaultTableMetaData($tab
$table = new PHPUnit_Extensions_Database_DataSet_DefaultTable($metaData);
foreach ($rows AS $row) {
$table->addRow($row);
}
$this->tables[$tableName] = $table;
}
}
protected function createIterator($reverse = false)
{
return new PHPUnit_Extensions_Database_DataSet_DefaultTableIterator($this->tables
}
public function getTable($tableName)
{
if (!isset($this->tables[$tableName])) {
throw new InvalidArgumentException("$tableName is not a table in the current
}
return $this->tables[$tableName];
}
}
?>
Query (SQL) DataSet (查询(SQL)数据集)
对于数据库断言,不仅需要有基于文件的 DataSet,同时也需要有一种内含数据库实际内容
的基于查询/SQL 的 DataSet。Query DataSet 在此闪亮登场:
<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook');
?>
单纯以名称来添加表是一种隐式地用以下查询来定义 DataTable 的方法:
<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook', 'SELECT * FROM guestbook');
?>
可以在这种用法中为你的表任意指定查询,例如限定行、列,或者加上 ORDER BY 子句:
<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook', 'SELECT id, content FROM guestbook ORDER BY created DESC');
?>
在关于数据库断言的那一节中有更多关于如何使用 Query DataSet 的细节。
59. 数据库测试
49
Database (DB) Dataset (数据库数据集)
通过访问测试所使用的数据库连接,可以自动创建包含数据库所有表以及其内容的
DataSet。所使用的数据库由数据库连接工厂方法的第二个参数指定。
可以像 testGuestbook() 中那样创建整个数据库所对应的 DataSet,或者像
testFilteredGuestbook() 方法中那样用一个白名单来将 DataSet 限制在若干表名的集
合上。
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class MySqlGuestbookTest extends TestCase
{
use TestCaseTrait;
/**
* @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
*/
public function getConnection()
{
$database = 'my_database';
$user = 'my_user';
$password = 'my_password';
$pdo = new PDO('mysql:...', $user, $password);
return $this->createDefaultDBConnection($pdo, $database);
}
public function testGuestbook()
{
$dataSet = $this->getConnection()->createDataSet();
// ...
}
public function testFilteredGuestbook()
{
$tableNames = ['guestbook'];
$dataSet = $this->getConnection()->createDataSet($tableNames);
// ...
}
}
?>
Replacement DataSet (替换数据集)
前面谈到了 Flat XML 和 CSV DataSet 所存在的 NULL 问题,不过有一种稍微有点复杂的解决
方法可以让这两种数据集都能正常处理 NULL。
Replacement DataSet 是已有数据集的修饰器(decorator),能够将数据集中任意列的值替换为
其他替代值。为了让留言本的例子能够处理 NULL 值,首先指定类似这样的文件:
<?xml version="1.0" ?>
<dataset>
<guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
<guestbook id="2" content="I like it!" user="##NULL##" created="2010-04-26 12:14:20"
</dataset>
然后将 Flat XML DataSet 包装在 Replacement DataSet 中:
<?php
use PHPUnitFrameworkTestCase;
60. 数据库测试
50
use PHPUnitDbUnitTestCaseTrait;
class ReplacementTest extends TestCase
{
use TestCaseTrait;
public function getDataSet()
{
$ds = $this->createFlatXmlDataSet('myFlatXmlFixture.xml');
$rds = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($ds);
$rds->addFullReplacement('##NULL##', null);
return $rds;
}
}
?>
DataSet Filter (数据集筛选器)
如果有一个非常大的基境文件,可以用数据集筛选器来为需要包含在子数据集中的表和列指
定白/黑名单。与 DB DataSet 联用来对数据集中的列进行筛选尤其方便。
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class DataSetFilterTest extends TestCase
{
use TestCaseTrait;
public function testIncludeFilteredGuestbook()
{
$tableNames = ['guestbook'];
$dataSet = $this->getConnection()->createDataSet();
$filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet);
$filterDataSet->addIncludeTables(['guestbook']);
$filterDataSet->setIncludeColumnsForTable('guestbook', ['id', 'content']);
// ..
}
public function testExcludeFilteredGuestbook()
{
$tableNames = ['guestbook'];
$dataSet = $this->getConnection()->createDataSet();
$filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet);
$filterDataSet->addExcludeTables(['foo', 'bar', 'baz']); // 只保留 guestbook 表!
$filterDataSet->setExcludeColumnsForTable('guestbook', ['user', 'created']);
// ..
}
}
?>
注意:不能对同一个表同时应用排除与包含两种列筛选器,只能分别应用于
不同的表。另外,表的白名单和黑名单也只能选择其一,不能二者同时使
用。
Composite DataSet (组合数据集)
Composite DataSet 能将多个已存在的数据集聚合成单个数据集,因此非常有用。如果多个数
据集中存在同样的表,其中的数据行将按照指定的顺序进行追加。例如,假设有两个数据
集, fixture1.xml:
61. 数据库测试
51
<?xml version="1.0" ?>
<dataset>
<guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
</dataset>
和 fixture2.xml:
<?xml version="1.0" ?>
<dataset>
<guestbook id="2" content="I like it!" user="##NULL##" created="2010-04-26 12:14:20"
</dataset>
通过 Composite DataSet 可以把这两个基境文件聚合在一起:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class CompositeTest extends TestCase
{
use TestCaseTrait;
public function getDataSet()
{
$ds1 = $this->createFlatXmlDataSet('fixture1.xml');
$ds2 = $this->createFlatXmlDataSet('fixture2.xml');
$compositeDs = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet();
$compositeDs->addDataSet($ds1);
$compositeDs->addDataSet($ds2);
return $compositeDs;
}
}
?>
当心外键
在建立基境的过程中, PHPUnit 的数据库扩展模块按照基境中所指定的顺序将数据行插入到
数据库内。假如数据库中使用了外键,这就意味着必须指定好表的顺序,以避免外键约束失
败。
实现自有的 DataSet/DataTable
为了理解 DataSet 和 DataTable 的内部实现,让我们来看看 DataSet 的接口。如果没打算自行
实现 DataSet 或者 DataTable,可以直接跳过这一部分。
<?php
interface PHPUnit_Extensions_Database_DataSet_IDataSet extends IteratorAggregate
{
public function getTableNames();
public function getTableMetaData($tableName);
public function getTable($tableName);
public function assertEquals(PHPUnit_Extensions_Database_DataSet_IDataSet $other);
public function getReverseIterator();
}
?>
这些 public 接口在数据库 TestCase 中 assertDataSetsEqual() 断言内使用,用以检测数
据集是否相等。IDataSet 中继承自 IteratorAggregate 接口的 getIterator() 方法用
62. 数据库测试
52
于对数据集中的所有表进行迭代。逆序迭代器让 PHPUnit 能够按照与创建时相反的顺序对所
有表执行 TRUNCATE 操作,以此来保证满足外键约束。
根据具体实现的不同,要采取不同的方法来将表实例添加到数据集中。例如,在所有基于文
件的数据集中,表都是在构造过程中直接从源文件生成并加入数据集中,比如
YamlDataSet、XmlDataSet 和 FlatXmlDataSet均是如此。
数据表则由以下接口表示:
<?php
interface PHPUnit_Extensions_Database_DataSet_ITable
{
public function getTableMetaData();
public function getRowCount();
public function getValue($row, $column);
public function getRow($row);
public function assertEquals(PHPUnit_Extensions_Database_DataSet_ITable $other);
}
?>
除了 getTableMetaData() 方法之外,这个接口是一目了然的。数据库扩展模块中的各种
断言(将于下一章中介绍)用到了所有这些方法,因此它们全部都是必需
的。getTableMetaData() 方法需要返回一个实现了
PHPUnit_Extensions_Database_DataSet_ITableMetaData 接口的描述表结构的对
象。这个对象包含如下信息:
• 表的名称
• 表的列名数组,按照列在结果集中出现的顺序排列。
• 构成主键的列的数组。
这个接口还包含有检验两个表的元数据实例是否彼此相等的断言,供数据集相等断言使用。
数据库连接 API
由数据库 TestCase 中的 getConnection() 方法所返回的连接接口有三个很有意思的方
法:
<?php
interface PHPUnit_Extensions_Database_DB_IDatabaseConnection
{
public function createDataSet(Array $tableNames = NULL);
public function createQueryTable($resultName, $sql);
public function getRowCount($tableName, $whereClause = NULL);
// ...
}
?>
1. createDataSet() 方法创建一个在数据集实现一节描述过的 Database (DB) DataSet(数
据库数据集)。
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class ConnectionTest extends TestCase
{
use TestCaseTrait;
63. 数据库测试
53
public function testCreateDataSet()
{
$tableNames = ['guestbook'];
$dataSet = $this->getConnection()->createDataSet();
}
}
?>
2. createQueryTable() 方法用于创建 QueryTable 的实例,需要为其指定结果名称和所使
用的 SQL 查询。当涉及到结果/表的断言(如后面关于数据库断言 API 那一节所示)时,
这个方法会很方便。
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class ConnectionTest extends TestCase
{
use TestCaseTrait;
public function testCreateQueryTable()
{
$tableNames = ['guestbook'];
$queryTable = $this->getConnection()->createQueryTable('guestbook', 'SELECT *
}
}
?>
3. getRowCount() 方法提供了一种方便的方式来取得表中的行数,并且还可以选择附加一
个 WHERE 子句来在计数前对数据行进行过滤。它可以和一个简单的相等断言合用:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class ConnectionTest extends TestCase
{
use TestCaseTrait;
public function testGetRowCount()
{
$this->assertEquals(2, $this->getConnection()->getRowCount('guestbook'));
}
}
?>
数据库断言 API
作为测试工具,数据库扩展模块理所当然会提供一些断言,可以用来验证数据库的当前状
态、表的当前状态、表中数据行的数量。本节将详细描述这部分功能:
对表中数据行的数量作出断言
很多时候,确认表中是否包含特定数量的数据行是非常有帮助的。可以轻松做到这一点,不
需要任何额外的使用连接 API 的粘合剂代码。比如说,在往留言本中插入一个新行之后,想
要确认在表中除了之前的例子中一直都有的两行之外还有第三行:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
64. 数据库测试
54
class GuestbookTest extends TestCase
{
use TestCaseTrait;
public function testAddEntry()
{
$this->assertEquals(2, $this->getConnection()->getRowCount('guestbook'), "Pre-Con
$guestbook = new Guestbook();
$guestbook->addEntry("suzy", "Hello world!");
$this->assertEquals(3, $this->getConnection()->getRowCount('guestbook'), "Inserti
}
}
?>
对表的状态作出断言
前面的这个断言很有帮助,但是肯定还想要检验表的实际内容,好核实是否所有值都写到了
正确的列中。可以通过表断言来做到这一点。
为此,先定义一个 QueryTable 实例,从表名称和 SQL 查询派生出其内容,随后将其与一个
基于文件/数组的数据集进行比较:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class GuestbookTest extends TestCase
{
use TestCaseTrait;
public function testAddEntry()
{
$guestbook = new Guestbook();
$guestbook->addEntry("suzy", "Hello world!");
$queryTable = $this->getConnection()->createQueryTable(
'guestbook', 'SELECT * FROM guestbook'
);
$expectedTable = $this->createFlatXmlDataSet("expectedBook.xml")
->getTable("guestbook");
$this->assertTablesEqual($expectedTable, $queryTable);
}
}
?>
现在需要为这个断言编写Flat XML 文件 expectedBook.xml:
<?xml version="1.0" ?>
<dataset>
<guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
<guestbook id="2" content="I like it!" user="nancy" created="2010-04-26 12:14:20" />
<guestbook id="3" content="Hello world!" user="suzy" created="2010-05-01 21:47:08" />
</dataset>
在整个时间长河中,只有特定的一秒钟内这个断言可以通过评定,在 2010–05–01 21:47:08。
在数据库测试中,日期构成了一个特殊的问题。可以从这个断言中省略 “created” 列来规避失
败。
为了让断言能得以通过, Flat XML 文件 expectedBook.xml 需要调整成大致类似这样:
65. 数据库测试
55
<?xml version="1.0" ?>
<dataset>
<guestbook id="1" content="Hello buddy!" user="joe" />
<guestbook id="2" content="I like it!" user="nancy" />
<guestbook id="3" content="Hello world!" user="suzy" />
</dataset>
还得修正一下 QueryTable 的调用:
<?php
$queryTable = $this->getConnection()->createQueryTable(
'guestbook', 'SELECT id, content, user FROM guestbook'
);
?>
对查询的结果作出断言
利用 QueryTable,也可以对复杂查询的结果作出断言,只需要指定查询以及结果名称,并
随后将其与某个数据集进行比较:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class ComplexQueryTest extends TestCase
{
use TestCaseTrait;
public function testComplexQuery()
{
$queryTable = $this->getConnection()->createQueryTable(
'myComplexQuery', 'SELECT complexQuery...'
);
$expectedTable = $this->createFlatXmlDataSet("complexQueryAssertion.xml")
->getTable("myComplexQuery");
$this->assertTablesEqual($expectedTable, $queryTable);
}
}
?>
对多个表的状态作出断言
当然可以一次性对多个表的状态作出断言,并将查询数据集与基于文件的数据集进行比较。
有两种不同的方式来进行数据集断言。
1. 可以从自数据库连接建立数据库数据集,并将其与基于文件的数据集进行比较。
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class DataSetAssertionsTest extends TestCase
{
use TestCaseTrait;
public function testCreateDataSetAssertion()
{
$dataSet = $this->getConnection()->createDataSet(['guestbook']);
$expectedDataSet = $this->createFlatXmlDataSet('guestbook.xml');
$this->assertDataSetsEqual($expectedDataSet, $dataSet);
}
66. 数据库测试
56
}
?>
2. 也可以自行构造数据集:
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitDbUnitTestCaseTrait;
class DataSetAssertionsTest extends TestCase
{
use TestCaseTrait;
public function testManualDataSetAssertion()
{
$dataSet = new PHPUnit_Extensions_Database_DataSet_QueryDataSet();
$dataSet->addTable('guestbook', 'SELECT id, content, user FROM guestbook'); //
$expectedDataSet = $this->createFlatXmlDataSet('guestbook.xml');
$this->assertDataSetsEqual($expectedDataSet, $dataSet);
}
}
?>
常见问题(FAQ)
PHPUnit 会为每个测试(重新)创建数据库吗?
不,PHPUnit 要求在测试套件开始时所有数据库对象必须全部可用。数据库、表、序列、触
发器还有视图,必须全部在运行测试套件之前创建好。
Doctrine 2 [http://www.doctrine-project.org] 或 eZ Components [http://www.ezcomponents.org] 拥
有强力的工具,可以按预定义的数据结构创建数据库,但是这些都必须和 PHPUnit 扩展模块
对接之后才能自动在整个测试套件运行之前重新创建数据库。
由于每个测试都会彻底清空数据库,因此无须为每个测试重新创建数据库。持久可用的数据
库同样能够完美工作。
为了让数据库扩展模块正常工作,需要在应用程序中使用
PDO 吗?
不,只在基境的清理与建立阶段还有断言检定时用到PDO。在你的自有代码中,可以使用任
意数据库抽象。
如果看到 “Too much Connections” 错误该怎么办?
如果没有对 TestCase 中 getConnection() 方法所创建 PDO 实例进行缓存,那么每个数据
库测试都会增加一个或多个数据库连接。MySQL的默认配置只允许100个并发连接,其他供
应商的数据库也都有各自的最大连接限制。
子章节 “使用你自己的抽象数据库 TestCase 类”展示了如何通过在所有测试中使用单个PDO
实例缓存来防止发生此错误。
Flat XML / CSV 数据集中如何处理 NULL?
别这么干。应当改用 XML 或者 YAML 数据集。
95. 扩展 PHPUnit
85
例 14.2. PHPUnit_Framework_Constraint_IsTrue 类
<?php
use PHPUnitFrameworkTestCase;
class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
{
/**
* 对参数 $other 进行约束评定。如果符合约束,
* 返回 TRUE,否则返回 FALSE。
*
* @param mixed $other Value or object to evaluate.
* @return bool
*/
public function matches($other)
{
return $other === true;
}
/**
* 返回代表此约束的字符串。
*
* @return string
*/
public function toString()
{
return 'is true';
}
}?>
在实现 assertTrue() 和 isTrue() 方法及
PHPUnit_Framework_Constraint_IsTrue 类时所付出的努力带来了一些好
处,assertThat() 能够自动负责起断言的评定与任务簿记(例如为了统计目的而对其进
行计数)工作。此外, isTrue() 方法还可以在配置仿件对象时用来作为匹配器。
实现 PHPUnitFrameworkTestListener
例 14.3 “简单的测试监听器”展示了 PHPUnitFrameworkTestListener 接口的一个
简单实现。
例 14.3. 简单的测试监听器
<?php
use PHPUnitFrameworkTestCase;
use PHPUnitFrameworkTestListener;
class SimpleTestListener implements TestListener
{
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Error while running test '%s'.n", $test->getName());
}
public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionF
{
printf("Test '%s' failed.n", $test->getName());
}
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' is incomplete.n", $test->getName());
96. 扩展 PHPUnit
86
}
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' is deemed risky.n", $test->getName());
}
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("Test '%s' has been skipped.n", $test->getName());
}
public function startTest(PHPUnit_Framework_Test $test)
{
printf("Test '%s' started.n", $test->getName());
}
public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf("Test '%s' ended.n", $test->getName());
}
public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' started.n", $suite->getName());
}
public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("TestSuite '%s' ended.n", $suite->getName());
}
}
?>
例 14.4 “使用测试监听器基类”展示了如何从抽象类
PHPUnit_Framework_BaseTestListener 派生子类,这个抽象类为所有接口方法提供
了空白实现,这样你就只需要指定那些在你的使用情境下有意义的接口方法。
例 14.4. 使用测试监听器基类
<?php
use PHPUnitFrameworkTestCase;
class ShortTestListener extends PHPUnit_Framework_BaseTestListener
{
public function endTest(PHPUnit_Framework_Test $test, $time)
{
printf("Test '%s' ended.n", $test->getName());
}
}
?>
在“测试监听器”一节中可以看到如何配置 PHPUnit 来将测试监听器附加到测试执行过程
上。
从 PHPUnit_Extensions_TestDecorator 派生
子类
可以将测试用例或者测试套件包装在 PHPUnit_Extensions_TestDecorator 的子类中
并运用 Decorator(修饰器)设计模式来在测试运行前后执行一些动作。
97. 扩展 PHPUnit
87
PHPUnit 了包含了一个具体的测试修饰器:PHPUnit_Extensions_RepeatedTest。它
用于重复运行某个测试,并且只在全部循环中都成功时计为成功。
例 14.5 “RepeatedTest 修饰器”展示了测试修饰器 PHPUnit_Extensions_RepeatedTest
的一个删减版本,用以说明如何编写你自己的测试修饰器。
例 14.5. RepeatedTest 修饰器
<?php
use PHPUnitFrameworkTestCase;
require_once 'PHPUnit/Extensions/TestDecorator.php';
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;
public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);
if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}
public function count()
{
return $this->timesRepeat * $this->test->count();
}
public function run(PHPUnit_Framework_TestResult $result = null)
{
if ($result === null) {
$result = $this->createResult();
}
for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}
return $result;
}
}
?>
实现 PHPUnit_Framework_Test
PHPUnit_Framework_Test 接口是比较狭义的,十分容易实现。举例来说,你可以自行
为 PHPUnit_Framework_Test 编写一个类似于 PHPUnitFrameworkTestCase 的实
现来运行数据驱动测试。
例 14.6 “一个数据驱动的测试”展示了一个数据驱动的测试用例类,对来自 CSV 文件内的
值进行比较。这个文件内的每个行看起来类似于 foo;bar,第一个值是期望值,第二个值
则是实际值。
例 14.6. 一个数据驱动的测试
<?php
use PHPUnitFrameworkTestCase;
98. 扩展 PHPUnit
88
class DataDrivenTest implements PHPUnit_Framework_Test
{
private $lines;
public function __construct($dataFile)
{
$this->lines = file($dataFile);
}
public function count()
{
return 1;
}
public function run(PHPUnit_Framework_TestResult $result = null)
{
if ($result === null) {
$result = new PHPUnit_Framework_TestResult;
}
foreach ($this->lines as $line) {
$result->startTest($this);
PHP_Timer::start();
$stopTime = null;
list($expected, $actual) = explode(';', $line);
try {
PHPUnit_Framework_Assert::assertEquals(
trim($expected), trim($actual)
);
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
$stopTime = PHP_Timer::stop();
$result->addFailure($this, $e, $stopTime);
}
catch (Exception $e) {
$stopTime = PHP_Timer::stop();
$result->addError($this, $e, $stopTime);
}
if ($stopTime === null) {
$stopTime = PHP_Timer::stop();
}
$result->endTest($this, $stopTime);
}
return $result;
}
}
$test = new DataDrivenTest('data_file.csv');
$result = PHPUnit_TextUI_TestRunner::run($test);
?>
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
.F
Time: 0 seconds
99. 扩展 PHPUnit
89
There was 1 failure:
1) DataDrivenTest
Failed asserting that two strings are equal.
expected string <bar>
difference < x>
got string <baz>
/home/sb/DataDrivenTest.php:32
/home/sb/DataDrivenTest.php:53
FAILURES!
Tests: 2, Failures: 1.
100. 90
附录 A. 断言
本附录列举可用的各种断言方法。
断言方法的用法:静态 vs. 非静态
PHPUnit 的各个断言是在 PHPUnitFrameworkAssert 中实现的。PHPUnit
FrameworkTestCase 则继承于 PHPUnitFrameworkAssert。
各个断言方法均声明为 static,可以从任何上下文以类似于 PHPUnitFramework
Assert::assertTrue() 的方式调用,或者也可以用类似于 $this->assertTrue()
或 self::assertTrue() 的方式在扩展自 PHPUnitFrameworkTestCase 的类内调
用。
实际上,只要(手工)包含了 PHPUnit 中的 src/Framework/Assert/Functions.php
源码文件,甚至可以在任何上下文中(甚至包括扩展自 PHPUnitFrameworkTestCase
的类中)以诸如 assertTrue() 这样的方式来调用全局函数封装。
有个常见的疑问——对于那些 PHPUnit 的新手尤甚——是究竟应该用诸如 $this-
>assertTrue() 还是诸如 self::assertTrue() 这样的形式来调用断言才是“正确的方
式”?简而言之:没有正确方式。同时,也没有错误方式。这基本上是个人喜好问题。
对于大多数人而言,由于测试方法是在测试对象上调用,因此用 $this->assertTrue()
会“觉的更正确”。然而请记住断言方法是声明为 static 的,这使其可以(重)用于测试
对象的作用域之外。最后,全局函数封装让开发者能再少打一些字(用 assertTrue() 代
替 $this->assertTrue() 或者 self::assertTrue())。
assertArrayHasKey()
assertArrayHasKey(mixed $key, array $array[, string $message = ''])
当 $array 不包含 $key 时报告错误,错误讯息由 $message 指定。
assertArrayNotHasKey() 是与之相反的断言,接受相同的参数。
例 A.1. assertArrayHasKey() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ArrayHasKeyTest extends TestCase
{
public function testFailure()
{
$this->assertArrayHasKey('foo', ['bar' => 'baz']);
}
}
?>
phpunit ArrayHasKeyTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
101. 断言
91
1) ArrayHasKeyTest::testFailure
Failed asserting that an array has the key 'foo'.
/home/sb/ArrayHasKeyTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertClassHasAttribute()
assertClassHasAttribute(string $attributeName, string $className[,
string $message = ''])
当 $className::attributeName 不存在时报告错误,错误讯息由 $message 指定。
assertClassNotHasAttribute() 是与之相反的断言,接受相同的参数。
例 A.2. assertClassHasAttribute() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ClassHasAttributeTest extends TestCase
{
public function testFailure()
{
$this->assertClassHasAttribute('foo', stdClass::class);
}
}
?>
phpunit ClassHasAttributeTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) ClassHasAttributeTest::testFailure
Failed asserting that class "stdClass" has attribute "foo".
/home/sb/ClassHasAttributeTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertArraySubset()
assertArraySubset(array $subset, array $array[, bool $strict = '',
string $message = ''])
当 $array 不包含 $subset 时报告错误,错误讯息由 $message 指定。
$strict 是一个标志,用于表明是否需要对数组中的对象进行全等判定。
例 A.3. assertArraySubset() 的用法
<?php
102. 断言
92
use PHPUnitFrameworkTestCase;
class ArraySubsetTest extends TestCase
{
public function testFailure()
{
$this->assertArraySubset(['config' => ['key-a', 'key-b']], ['config' => ['key-a']
}
}
?>
phpunit ArrayHasKeyTest
PHPUnit 4.4.0 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) EpilogEpilogTest::testNoFollowOption
Failed asserting that an array has the subset Array &0 (
'config' => Array &1 (
0 => 'key-a'
1 => 'key-b'
)
).
/home/sb/ArraySubsetTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertClassHasStaticAttribute()
assertClassHasStaticAttribute(string $attributeName, string
$className[, string $message = ''])
当 $className::attributeName 不存在时报告错误,错误讯息由 $message 指定。
assertClassNotHasStaticAttribute() 是与之相反的断言,接受相同的参数。
例 A.4. assertClassHasStaticAttribute() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ClassHasStaticAttributeTest extends TestCase
{
public function testFailure()
{
$this->assertClassHasStaticAttribute('foo', stdClass::class);
}
}
?>
phpunit ClassHasStaticAttributeTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
103. 断言
93
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) ClassHasStaticAttributeTest::testFailure
Failed asserting that class "stdClass" has static attribute "foo".
/home/sb/ClassHasStaticAttributeTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertContains()
assertContains(mixed $needle, Iterator|array $haystack[, string
$message = ''])
当 $needle 不是 $haystack的元素时报告错误,错误讯息由 $message 指定。
assertNotContains() 是与之相反的断言,接受相同的参数。
assertAttributeContains() 和 assertAttributeNotContains() 是便捷包装
(convenience wrapper),以某个类或对象的 public、protected 或 private 属性为搜索
范围。
例 A.5. assertContains() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ContainsTest extends TestCase
{
public function testFailure()
{
$this->assertContains(4, [1, 2, 3]);
}
}
?>
phpunit ContainsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) ContainsTest::testFailure
Failed asserting that an array contains 4.
/home/sb/ContainsTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertContains(string $needle, string $haystack[, string $message =
'', boolean $ignoreCase = false])
当 $needle 不是 $haystack 的子字符串时报告错误,错误讯息由 $message 指定。
104. 断言
94
如果 $ignoreCase 为 true,测试将按大小写不敏感的方式进行。
例 A.6. assertContains() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ContainsTest extends TestCase
{
public function testFailure()
{
$this->assertContains('baz', 'foobar');
}
}
?>
phpunit ContainsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) ContainsTest::testFailure
Failed asserting that 'foobar' contains "baz".
/home/sb/ContainsTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
例 A.7. 带有 $ignoreCase 参数的 assertContains() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ContainsTest extends TestCase
{
public function testFailure()
{
$this->assertContains('foo', 'FooBar');
}
public function testOK()
{
$this->assertContains('foo', 'FooBar', '', true);
}
}
?>
phpunit ContainsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F.
Time: 0 seconds, Memory: 2.75Mb
There was 1 failure:
105. 断言
95
1) ContainsTest::testFailure
Failed asserting that 'FooBar' contains "foo".
/home/sb/ContainsTest.php:6
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
assertContainsOnly()
assertContainsOnly(string $type, Iterator|array $haystack[, boolean
$isNativeType = null, string $message = ''])
当 $haystack 并非仅包含类型为 $type 的变量时报告错误,错误讯息由 $message 指定。
$isNativeType 是一个标志,用来表明 $type 是否是原生 PHP 类型。
assertNotContainsOnly() 是与之相反的断言,并接受相同的参数。
assertAttributeContainsOnly() 和 assertAttributeNotContainsOnly() 是便
捷包装(convenience wrapper),以某个类或对象的 public、protected 或 private 属性
为搜索范围。
例 A.8. assertContainsOnly() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ContainsOnlyTest extends TestCase
{
public function testFailure()
{
$this->assertContainsOnly('string', ['1', '2', 3]);
}
}
?>
phpunit ContainsOnlyTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) ContainsOnlyTest::testFailure
Failed asserting that Array (
0 => '1'
1 => '2'
2 => 3
) contains only values of type "string".
/home/sb/ContainsOnlyTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertContainsOnlyInstancesOf()
106. 断言
96
assertContainsOnlyInstancesOf(string $classname, Traversable|array
$haystack[, string $message = ''])
当 $haystack 并非仅包含类 $classname 的实例时报告错误,错误讯息由 $message 指
定。
例 A.9. assertContainsOnlyInstancesOf() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ContainsOnlyInstancesOfTest extends TestCase
{
public function testFailure()
{
$this->assertContainsOnlyInstancesOf(
Foo::class,
[new Foo, new Bar, new Foo]
);
}
}
?>
phpunit ContainsOnlyInstancesOfTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) ContainsOnlyInstancesOfTest::testFailure
Failed asserting that Array ([0]=> Bar Object(...)) is an instance of class "Foo".
/home/sb/ContainsOnlyInstancesOfTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertCount()
assertCount($expectedCount, $haystack[, string $message = ''])
当 $haystack 中的元素数量不是 $expectedCount 时报告错误,错误讯息由 $message
指定。
assertNotCount() 是与之相反的断言,接受相同的参数。
例 A.10. assertCount() 的用法
<?php
use PHPUnitFrameworkTestCase;
class CountTest extends TestCase
{
public function testFailure()
{
$this->assertCount(0, ['foo']);
}
107. 断言
97
}
?>
phpunit CountTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) CountTest::testFailure
Failed asserting that actual size 1 matches expected size 0.
/home/sb/CountTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertDirectoryExists()
assertDirectoryExists(string $directory[, string $message = ''])
当 $directory 所指定的目录不存在时报告错误,错误讯息由 $message 指定。
assertDirectoryNotExists() 是与之相反的断言,并接受相同的参数。
例 A.11. assertDirectoryExists() 的用法
<?php
use PHPUnitFrameworkTestCase;
class DirectoryExistsTest extends TestCase
{
public function testFailure()
{
$this->assertDirectoryExists('/path/to/directory');
}
}
?>
phpunit DirectoryExistsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) DirectoryExistsTest::testFailure
Failed asserting that directory "/path/to/directory" exists.
/home/sb/DirectoryExistsTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertDirectoryIsReadable()
108. 断言
98
assertDirectoryIsReadable(string $directory[, string $message = ''])
当 $directory 所指定的目录不是个目录或不可读时报告错误,错误讯息由 $message 指
定。
assertDirectoryNotIsReadable() 是与之相反的断言,并接受相同的参数。
例 A.12. assertDirectoryIsReadable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class DirectoryIsReadableTest extends TestCase
{
public function testFailure()
{
$this->assertDirectoryIsReadable('/path/to/directory');
}
}
?>
phpunit DirectoryIsReadableTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) DirectoryIsReadableTest::testFailure
Failed asserting that "/path/to/directory" is readable.
/home/sb/DirectoryIsReadableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertDirectoryIsWritable()
assertDirectoryIsWritable(string $directory[, string $message = ''])
当 $directory 所指定的目录不是个目录或不可写时报告错误,错误讯息由 $message 指
定。
assertDirectoryNotIsWritable() 是与之相反的断言,并接受相同的参数。
例 A.13. assertDirectoryIsWritable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class DirectoryIsWritableTest extends TestCase
{
public function testFailure()
{
$this->assertDirectoryIsWritable('/path/to/directory');
}
}
109. 断言
99
?>
phpunit DirectoryIsWritableTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) DirectoryIsWritableTest::testFailure
Failed asserting that "/path/to/directory" is writable.
/home/sb/DirectoryIsWritableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertEmpty()
assertEmpty(mixed $actual[, string $message = ''])
当 $actual 非空时报告错误,错误讯息由 $message 指定。
assertNotEmpty() 是与之相反的断言,接受相同的参数。
assertAttributeEmpty() 和 assertAttributeNotEmpty() 是便捷包装(convenience
wrapper),可以应用于某个类或对象的某个 public、protected 或 private 属性。
例 A.14. assertEmpty() 的用法
<?php
use PHPUnitFrameworkTestCase;
class EmptyTest extends TestCase
{
public function testFailure()
{
$this->assertEmpty(['foo']);
}
}
?>
phpunit EmptyTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) EmptyTest::testFailure
Failed asserting that an array is empty.
/home/sb/EmptyTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
110. 断言
100
assertEqualXMLStructure()
assertEqualXMLStructure(DOMElement $expectedElement, DOMElement
$actualElement[, boolean $checkAttributes = false, string $message
= ''])
当 $actualElement 中 DOMElement 的 XML 结构与 $expectedElement 中 DOMElement
的 XML 结构不相同时报告错误,错误讯息由 $message 指定。
例 A.15. assertEqualXMLStructure() 的用法
<?php
use PHPUnitFrameworkTestCase;
class EqualXMLStructureTest extends TestCase
{
public function testFailureWithDifferentNodeNames()
{
$expected = new DOMElement('foo');
$actual = new DOMElement('bar');
$this->assertEqualXMLStructure($expected, $actual);
}
public function testFailureWithDifferentNodeAttributes()
{
$expected = new DOMDocument;
$expected->loadXML('<foo bar="true" />');
$actual = new DOMDocument;
$actual->loadXML('<foo/>');
$this->assertEqualXMLStructure(
$expected->firstChild, $actual->firstChild, true
);
}
public function testFailureWithDifferentChildrenCount()
{
$expected = new DOMDocument;
$expected->loadXML('<foo><bar/><bar/><bar/></foo>');
$actual = new DOMDocument;
$actual->loadXML('<foo><bar/></foo>');
$this->assertEqualXMLStructure(
$expected->firstChild, $actual->firstChild
);
}
public function testFailureWithDifferentChildren()
{
$expected = new DOMDocument;
$expected->loadXML('<foo><bar/><bar/><bar/></foo>');
$actual = new DOMDocument;
$actual->loadXML('<foo><baz/><baz/><baz/></foo>');
$this->assertEqualXMLStructure(
$expected->firstChild, $actual->firstChild
);
}
}
111. 断言
101
?>
phpunit EqualXMLStructureTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
FFFF
Time: 0 seconds, Memory: 5.75Mb
There were 4 failures:
1) EqualXMLStructureTest::testFailureWithDifferentNodeNames
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'foo'
+'bar'
/home/sb/EqualXMLStructureTest.php:9
2) EqualXMLStructureTest::testFailureWithDifferentNodeAttributes
Number of attributes on node "foo" does not match
Failed asserting that 0 matches expected 1.
/home/sb/EqualXMLStructureTest.php:22
3) EqualXMLStructureTest::testFailureWithDifferentChildrenCount
Number of child nodes of "foo" differs
Failed asserting that 1 matches expected 3.
/home/sb/EqualXMLStructureTest.php:35
4) EqualXMLStructureTest::testFailureWithDifferentChildren
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'
/home/sb/EqualXMLStructureTest.php:48
FAILURES!
Tests: 4, Assertions: 8, Failures: 4.
assertEquals()
assertEquals(mixed $expected, mixed $actual[, string $message = ''])
当两个变量 $expected 和 $actual 不相等时报告错误,错误讯息由 $message 指定。
assertNotEquals() 是与之相反的断言,接受相同的参数。
assertAttributeEquals() 和 assertAttributeNotEquals() 是便捷包装
(convenience wrapper),以某个类或对象的某个 public、protected 或 private 属性作
为实际值来进行比较。
例 A.16. assertEquals() 的用法
<?php
use PHPUnitFrameworkTestCase;
112. 断言
102
class EqualsTest extends TestCase
{
public function testFailure()
{
$this->assertEquals(1, 0);
}
public function testFailure2()
{
$this->assertEquals('bar', 'baz');
}
public function testFailure3()
{
$this->assertEquals("foonbarnbazn", "foonbahnbazn");
}
}
?>
phpunit EqualsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
FFF
Time: 0 seconds, Memory: 5.25Mb
There were 3 failures:
1) EqualsTest::testFailure
Failed asserting that 0 matches expected 1.
/home/sb/EqualsTest.php:6
2) EqualsTest::testFailure2
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'
/home/sb/EqualsTest.php:11
3) EqualsTest::testFailure3
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
'foo
-bar
+bah
baz
'
/home/sb/EqualsTest.php:16
FAILURES!
Tests: 3, Assertions: 3, Failures: 3.
如果 $expected 和 $actual 是某些特定的类型,将使用更加专门的比较方式,参阅下文。
assertEquals(float $expected, float $actual[, string $message = '',
float $delta = 0])
113. 断言
103
当两个浮点数 $expected 和 $actual 之间的差值(的绝对值)大于 $delta 时报告错误,
错误讯息由 $message 指定。
关于为什么 $delta 参数是必须的,请阅读《关于浮点运算,每一位计算机科学从业人员都
应该知道的事实 [http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html]》。
例 A.17. 将assertEquals()用于浮点数时的用法
<?php
use PHPUnitFrameworkTestCase;
class EqualsTest extends TestCase
{
public function testSuccess()
{
$this->assertEquals(1.0, 1.1, '', 0.2);
}
public function testFailure()
{
$this->assertEquals(1.0, 1.1);
}
}
?>
phpunit EqualsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
.F
Time: 0 seconds, Memory: 5.75Mb
There was 1 failure:
1) EqualsTest::testFailure
Failed asserting that 1.1 matches expected 1.0.
/home/sb/EqualsTest.php:11
FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
assertEquals(DOMDocument $expected, DOMDocument $actual[, string
$message = ''])
当 $expected 和 $actual 这两个 DOMDocument 对象所表示的 XML 文档对应的无注释规
范形式不相同时报告错误,错误讯息由 $message 指定。
例 A.18. assertEquals()应用于 DOMDocument 对象时的用法
<?php
use PHPUnitFrameworkTestCase;
class EqualsTest extends TestCase
{
public function testFailure()
{
$expected = new DOMDocument;
$expected->loadXML('<foo><bar/></foo>');
$actual = new DOMDocument;
$actual->loadXML('<bar><foo/></bar>');
114. 断言
104
$this->assertEquals($expected, $actual);
}
}
?>
phpunit EqualsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) EqualsTest::testFailure
Failed asserting that two DOM documents are equal.
--- Expected
+++ Actual
@@ @@
<?xml version="1.0"?>
-<foo>
- <bar/>
-</foo>
+<bar>
+ <foo/>
+</bar>
/home/sb/EqualsTest.php:12
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertEquals(object $expected, object $actual[, string $message =
''])
当 $expected 和 $actual 这两个对象的属性值不相等时报告错误,错误讯息由 $message
指定。
例 A.19. assertEquals()应用于对象时的用法
<?php
use PHPUnitFrameworkTestCase;
class EqualsTest extends TestCase
{
public function testFailure()
{
$expected = new stdClass;
$expected->foo = 'foo';
$expected->bar = 'bar';
$actual = new stdClass;
$actual->foo = 'bar';
$actual->baz = 'bar';
$this->assertEquals($expected, $actual);
}
}
?>
phpunit EqualsTest
115. 断言
105
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) EqualsTest::testFailure
Failed asserting that two objects are equal.
--- Expected
+++ Actual
@@ @@
stdClass Object (
- 'foo' => 'foo'
- 'bar' => 'bar'
+ 'foo' => 'bar'
+ 'baz' => 'bar'
)
/home/sb/EqualsTest.php:14
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertEquals(array $expected, array $actual[, string $message = ''])
当 $expected 和 $actual 这两个数组不相等时报告错误,错误讯息由 $message 指定。
例 A.20. assertEquals() 应用于数组时的用法
<?php
use PHPUnitFrameworkTestCase;
class EqualsTest extends TestCase
{
public function testFailure()
{
$this->assertEquals(['a', 'b', 'c'], ['a', 'c', 'd']);
}
}
?>
phpunit EqualsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) EqualsTest::testFailure
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
0 => 'a'
- 1 => 'b'
- 2 => 'c'
+ 1 => 'c'
+ 2 => 'd'
116. 断言
106
)
/home/sb/EqualsTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertFalse()
assertFalse(bool $condition[, string $message = ''])
当 $condition 为 true 时报告错误,错误讯息由 $message 指定。
assertNotFalse() 是与之相反的断言,接受相同的参数。
例 A.21. assertFalse() 的用法
<?php
use PHPUnitFrameworkTestCase;
class FalseTest extends TestCase
{
public function testFailure()
{
$this->assertFalse(true);
}
}
?>
phpunit FalseTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) FalseTest::testFailure
Failed asserting that true is false.
/home/sb/FalseTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertFileEquals()
assertFileEquals(string $expected, string $actual[, string $message
= ''])
当 $expected 所指定的文件与 $actual 所指定的文件内容不同时报告错误,错误讯息由
$message 指定。
assertFileNotEquals() 是与之相反的断言,接受相同的参数。
例 A.22. assertFileEquals() 的用法
<?php
117. 断言
107
use PHPUnitFrameworkTestCase;
class FileEqualsTest extends TestCase
{
public function testFailure()
{
$this->assertFileEquals('/home/sb/expected', '/home/sb/actual');
}
}
?>
phpunit FileEqualsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) FileEqualsTest::testFailure
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'expected
+'actual
'
/home/sb/FileEqualsTest.php:6
FAILURES!
Tests: 1, Assertions: 3, Failures: 1.
assertFileExists()
assertFileExists(string $filename[, string $message = ''])
当 $filename 所指定的文件不存在时报告错误,错误讯息由 $message 指定。
assertFileNotExists() 是与之相反的断言,接受相同的参数。
例 A.23. assertFileExists() 的用法
<?php
use PHPUnitFrameworkTestCase;
class FileExistsTest extends TestCase
{
public function testFailure()
{
$this->assertFileExists('/path/to/file');
}
}
?>
phpunit FileExistsTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
118. 断言
108
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) FileExistsTest::testFailure
Failed asserting that file "/path/to/file" exists.
/home/sb/FileExistsTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertFileIsReadable()
assertFileIsReadable(string $filename[, string $message = ''])
当 $filename 所指定的文件不是个文件或不可读时报告错误,错误讯息由 $message 指
定。
assertFileNotIsReadable() 是与之相反的断言,并接受相同的参数。
例 A.24. assertFileIsReadable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class FileIsReadableTest extends TestCase
{
public function testFailure()
{
$this->assertFileIsReadable('/path/to/file');
}
}
?>
phpunit FileIsReadableTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) FileIsReadableTest::testFailure
Failed asserting that "/path/to/file" is readable.
/home/sb/FileIsReadableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertFileIsWritable()
assertFileIsWritable(string $filename[, string $message = ''])
当 $filename 所指定的文件不是个文件或不可写时报告错误,错误讯息由 $message 指
定。
assertFileNotIsWritable() 是与之相反的断言,并接受相同的参数。
119. 断言
109
例 A.25. assertFileIsWritable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class FileIsWritableTest extends TestCase
{
public function testFailure()
{
$this->assertFileIsWritable('/path/to/file');
}
}
?>
phpunit FileIsWritableTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) FileIsWritableTest::testFailure
Failed asserting that "/path/to/file" is writable.
/home/sb/FileIsWritableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertGreaterThan()
assertGreaterThan(mixed $expected, mixed $actual[, string $message
= ''])
当 $actual 的值不大于 $expected 的值时报告错误,错误讯息由 $message 指定。
assertAttributeGreaterThan() 是便捷包装(convenience wrapper),以某个类或对象
的某个 public、protected 或 private 属性作为实际值来进行比较。
例 A.26. assertGreaterThan() 的用法
<?php
use PHPUnitFrameworkTestCase;
class GreaterThanTest extends TestCase
{
public function testFailure()
{
$this->assertGreaterThan(2, 1);
}
}
?>
phpunit GreaterThanTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
120. 断言
110
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) GreaterThanTest::testFailure
Failed asserting that 1 is greater than 2.
/home/sb/GreaterThanTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertGreaterThanOrEqual()
assertGreaterThanOrEqual(mixed $expected, mixed $actual[, string
$message = ''])
当 $actual 的值不大于且不等于 $expected 的值时报告错误,错误讯息由 $message 指
定。
assertAttributeGreaterThanOrEqual() 是便捷包装(convenience wrapper),以某个
类或对象的某个 public、protected 或 private 属性作为实际值来进行比较。
例 A.27. assertGreaterThanOrEqual() 的用法
<?php
use PHPUnitFrameworkTestCase;
class GreatThanOrEqualTest extends TestCase
{
public function testFailure()
{
$this->assertGreaterThanOrEqual(2, 1);
}
}
?>
phpunit GreaterThanOrEqualTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) GreatThanOrEqualTest::testFailure
Failed asserting that 1 is equal to 2 or is greater than 2.
/home/sb/GreaterThanOrEqualTest.php:6
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
assertInfinite()
assertInfinite(mixed $variable[, string $message = ''])
当 $actual 不是 INF 时报告错误,错误讯息由 $message 指定。
121. 断言
111
assertFinite() 是与之相反的断言,接受相同的参数。
例 A.28. assertInfinite() 的用法
<?php
use PHPUnitFrameworkTestCase;
class InfiniteTest extends TestCase
{
public function testFailure()
{
$this->assertInfinite(1);
}
}
?>
phpunit InfiniteTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) InfiniteTest::testFailure
Failed asserting that 1 is infinite.
/home/sb/InfiniteTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertInstanceOf()
assertInstanceOf($expected, $actual[, $message = ''])
当 $actual 不是 $expected 的实例时报告错误,错误讯息由 $message 指定。
assertNotInstanceOf() 是与之相反的断言,接受相同的参数。
assertAttributeInstanceOf() 和 assertAttributeNotInstanceOf() 是便捷
包装(convenience wrapper),可以应用于某个类或对象的某个 public、protected 或
private 属性。
例 A.29. assertInstanceOf() 的用法
<?php
use PHPUnitFrameworkTestCase;
class InstanceOfTest extends TestCase
{
public function testFailure()
{
$this->assertInstanceOf(RuntimeException::class, new Exception);
}
}
?>
phpunit InstanceOfTest
122. 断言
112
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) InstanceOfTest::testFailure
Failed asserting that Exception Object (...) is an instance of class "RuntimeException".
/home/sb/InstanceOfTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertInternalType()
assertInternalType($expected, $actual[, $message = ''])
当 $actual 不是 $expected 所指明的类型时报告错误,错误讯息由 $message 指定。
assertNotInternalType() 是与之相反的断言,接受相同的参数。
assertAttributeInternalType() 和 assertAttributeNotInternalType() 是
便捷包装(convenience wrapper),可以应用于某个类或对象的某个 public、protected 或
private 属性。
例 A.30. assertInternalType() 的用法
<?php
use PHPUnitFrameworkTestCase;
class InternalTypeTest extends TestCase
{
public function testFailure()
{
$this->assertInternalType('string', 42);
}
}
?>
phpunit InternalTypeTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) InternalTypeTest::testFailure
Failed asserting that 42 is of type "string".
/home/sb/InternalTypeTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertIsReadable()
123. 断言
113
assertIsReadable(string $filename[, string $message = ''])
当 $filename 所指定的文件或目录不可读时报告错误,错误讯息由 $message 指定。
assertNotIsReadable() 是与之相反的断言,并接受相同的参数。
例 A.31. assertIsReadable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class IsReadableTest extends TestCase
{
public function testFailure()
{
$this->assertIsReadable('/path/to/unreadable');
}
}
?>
phpunit IsReadableTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) IsReadableTest::testFailure
Failed asserting that "/path/to/unreadable" is readable.
/home/sb/IsReadableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertIsWritable()
assertIsWritable(string $filename[, string $message = ''])
当 $filename 所指定的文件或目录不可写时报告错误,错误讯息由 $message 指定。
assertNotIsWritable() 是与之相反的断言,并接受相同的参数。
例 A.32. assertIsWritable() 的用法
<?php
use PHPUnitFrameworkTestCase;
class IsWritableTest extends TestCase
{
public function testFailure()
{
$this->assertIsWritable('/path/to/unwritable');
}
}
?>
phpunit IsWritableTest
124. 断言
114
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) IsWritableTest::testFailure
Failed asserting that "/path/to/unwritable" is writable.
/home/sb/IsWritableTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertJsonFileEqualsJsonFile()
assertJsonFileEqualsJsonFile(mixed $expectedFile, mixed
$actualFile[, string $message = ''])
当 $actualFile 对应的值与 $expectedFile 对应的值不匹配时报告错误,错误讯息由
$message 指定。
例 A.33. assertJsonFileEqualsJsonFile() 的用法
<?php
use PHPUnitFrameworkTestCase;
class JsonFileEqualsJsonFileTest extends TestCase
{
public function testFailure()
{
$this->assertJsonFileEqualsJsonFile(
'path/to/fixture/file', 'path/to/actual/file');
}
}
?>
phpunit JsonFileEqualsJsonFileTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) JsonFileEqualsJsonFile::testFailure
Failed asserting that '{"Mascot":"Tux"}' matches JSON string "["Mascott", "Tux", "OS", "L
/home/sb/JsonFileEqualsJsonFileTest.php:5
FAILURES!
Tests: 1, Assertions: 3, Failures: 1.
assertJsonStringEqualsJsonFile()
assertJsonStringEqualsJsonFile(mixed $expectedFile, mixed
$actualJson[, string $message = ''])
125. 断言
115
当 $actualJson 对应的值与 $expectedFile 对应的值不匹配时报告错误,错误讯息由
$message 指定。
例 A.34. assertJsonStringEqualsJsonFile() 的用法
<?php
use PHPUnitFrameworkTestCase;
class JsonStringEqualsJsonFileTest extends TestCase
{
public function testFailure()
{
$this->assertJsonStringEqualsJsonFile(
'path/to/fixture/file', json_encode(['Mascot' => 'ux'])
);
}
}
?>
phpunit JsonStringEqualsJsonFileTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) JsonStringEqualsJsonFile::testFailure
Failed asserting that '{"Mascot":"ux"}' matches JSON string "{"Mascott":"Tux"}".
/home/sb/JsonStringEqualsJsonFileTest.php:5
FAILURES!
Tests: 1, Assertions: 3, Failures: 1.
assertJsonStringEqualsJsonString()
assertJsonStringEqualsJsonString(mixed $expectedJson, mixed
$actualJson[, string $message = ''])
当 $actualJson 对应的值与 $expectedJson 对应的值不匹配时报告错误,错误讯息由
$message 指定。
例 A.35. assertJsonStringEqualsJsonString() 的用法
<?php
use PHPUnitFrameworkTestCase;
class JsonStringEqualsJsonStringTest extends TestCase
{
public function testFailure()
{
$this->assertJsonStringEqualsJsonString(
json_encode(['Mascot' => 'Tux']),
json_encode(['Mascot' => 'ux'])
);
}
}
?>
126. 断言
116
phpunit JsonStringEqualsJsonStringTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) JsonStringEqualsJsonStringTest::testFailure
Failed asserting that two objects are equal.
--- Expected
+++ Actual
@@ @@
stdClass Object (
- 'Mascot' => 'Tux'
+ 'Mascot' => 'ux'
)
/home/sb/JsonStringEqualsJsonStringTest.php:5
FAILURES!
Tests: 1, Assertions: 3, Failures: 1.
assertLessThan()
assertLessThan(mixed $expected, mixed $actual[, string $message =
''])
当 $actual 的值不小于 $expected 的值时报告错误,错误讯息由 $message 指定。
assertAttributeLessThan() 是便捷包装(convenience wrapper),以某个类或对象的某
个 public、protected 或 private 属性作为实际值来进行比较。
例 A.36. assertLessThan() 的用法
<?php
use PHPUnitFrameworkTestCase;
class LessThanTest extends TestCase
{
public function testFailure()
{
$this->assertLessThan(1, 2);
}
}
?>
phpunit LessThanTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) LessThanTest::testFailure
Failed asserting that 2 is less than 1.
/home/sb/LessThanTest.php:6
127. 断言
117
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertLessThanOrEqual()
assertLessThanOrEqual(mixed $expected, mixed $actual[, string
$message = ''])
当 $actual 的值不小于且不等于 $expected 的值时报告错误,错误讯息由 $message 指
定。
assertAttributeLessThanOrEqual() 是便捷包装(convenience wrapper),以某个类或
对象的某个 public、protected 或 private 属性作为实际值来进行比较。
例 A.37. assertLessThanOrEqual() 的用法
<?php
use PHPUnitFrameworkTestCase;
class LessThanOrEqualTest extends TestCase
{
public function testFailure()
{
$this->assertLessThanOrEqual(1, 2);
}
}
?>
phpunit LessThanOrEqualTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) LessThanOrEqualTest::testFailure
Failed asserting that 2 is equal to 1 or is less than 1.
/home/sb/LessThanOrEqualTest.php:6
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
assertNan()
assertNan(mixed $variable[, string $message = ''])
当 $variable 不是 NAN 时报告错误,错误讯息由 $message 指定。
例 A.38. assertNan() 的用法
<?php
use PHPUnitFrameworkTestCase;
class NanTest extends TestCase
{
128. 断言
118
public function testFailure()
{
$this->assertNan(1);
}
}
?>
phpunit NanTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) NanTest::testFailure
Failed asserting that 1 is nan.
/home/sb/NanTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertNull()
assertNull(mixed $variable[, string $message = ''])
当 $actual 不是 null 时报告错误,错误讯息由 $message 指定。
assertNotNull() 是与之相反的断言,接受相同的参数。
例 A.39. assertNull() 的使用
<?php
use PHPUnitFrameworkTestCase;
class NullTest extends TestCase
{
public function testFailure()
{
$this->assertNull('foo');
}
}
?>
phpunit NotNullTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) NullTest::testFailure
Failed asserting that 'foo' is null.
/home/sb/NotNullTest.php:6
FAILURES!
129. 断言
119
Tests: 1, Assertions: 1, Failures: 1.
assertObjectHasAttribute()
assertObjectHasAttribute(string $attributeName, object $object[,
string $message = ''])
当 $object->attributeName 不存在时报告错误,错误讯息由 $message 指定。
assertObjectNotHasAttribute() 是与之相反的断言,接受相同的参数。
例 A.40. assertObjectHasAttribute() 的用法
<?php
use PHPUnitFrameworkTestCase;
class ObjectHasAttributeTest extends TestCase
{
public function testFailure()
{
$this->assertObjectHasAttribute('foo', new stdClass);
}
}
?>
phpunit ObjectHasAttributeTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) ObjectHasAttributeTest::testFailure
Failed asserting that object of class "stdClass" has attribute "foo".
/home/sb/ObjectHasAttributeTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertRegExp()
assertRegExp(string $pattern, string $string[, string $message = ''])
当 $string 不匹配于正则表达式 $pattern 时报告错误,错误讯息由 $message 指定。
assertNotRegExp() 是与之相反的断言,接受相同的参数。
例 A.41. assertRegExp() 的用法
<?php
use PHPUnitFrameworkTestCase;
class RegExpTest extends TestCase
{
public function testFailure()
{
130. 断言
120
$this->assertRegExp('/foo/', 'bar');
}
}
?>
phpunit RegExpTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) RegExpTest::testFailure
Failed asserting that 'bar' matches PCRE pattern "/foo/".
/home/sb/RegExpTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertStringMatchesFormat()
assertStringMatchesFormat(string $format, string $string[, string
$message = ''])
当 $string 不匹配于 $format 定义的格式时报告错误,错误讯息由 $message 指定。
assertStringNotMatchesFormat() 是与之相反的断言,接受相同的参数。
例 A.42. assertStringMatchesFormat() 的用法
<?php
use PHPUnitFrameworkTestCase;
class StringMatchesFormatTest extends TestCase
{
public function testFailure()
{
$this->assertStringMatchesFormat('%i', 'foo');
}
}
?>
phpunit StringMatchesFormatTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) StringMatchesFormatTest::testFailure
Failed asserting that 'foo' matches PCRE pattern "/^[+-]?d+$/s".
/home/sb/StringMatchesFormatTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
131. 断言
121
格式定义字符串中可以使用如下占位符:
• %e:表示目录分隔符,例如在 Linux 系统中是 /。
• %s:一个或多个除了换行符以外的任意字符(非空白字符或者空白字符)。
• %S:零个或多个除了换行符以外的任意字符(非空白字符或者空白字符)。
• %a:一个或多个包括换行符在内的任意字符(非空白字符或者空白字符)。
• %A:零个或多个包括换行符在内的任意字符(非空白字符或者空白字符)。
• %w:零个或多个空白字符。
• %i:带符号整数值,例如 +3142、-3142。
• %d:无符号整数值,例如 123456。
• %x:一个或多个十六进制字符。所谓十六进制字符,指的是在以下范围内的字
符:0-9、a-f、A-F。
• %f:浮点数,例如 3.142、-3.142、3.142E-10、3.142e+10。
• %c:单个任意字符。
assertStringMatchesFormatFile()
assertStringMatchesFormatFile(string $formatFile, string $string[,
string $message = ''])
当 $string 不匹配于 $formatFile 的内容所定义的格式时报告错误,错误讯息由
$message 指定。
assertStringNotMatchesFormatFile() 是与之相反的断言,接受相同的参数。
例 A.43. assertStringMatchesFormatFile() 的用法
<?php
use PHPUnitFrameworkTestCase;
class StringMatchesFormatFileTest extends TestCase
{
public function testFailure()
{
$this->assertStringMatchesFormatFile('/path/to/expected.txt', 'foo');
}
}
?>
phpunit StringMatchesFormatFileTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) StringMatchesFormatFileTest::testFailure
Failed asserting that 'foo' matches PCRE pattern "/^[+-]?d+
$/s".
132. 断言
122
/home/sb/StringMatchesFormatFileTest.php:6
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
assertSame()
assertSame(mixed $expected, mixed $actual[, string $message = ''])
当两个变量 $expected 和 $actual 的值与类型不完全相同时报告错误,错误讯息由
$message 指定。
assertNotSame() 是与之相反的断言,接受相同的参数。
assertAttributeSame() 和 assertAttributeNotSame() 是便捷包装(convenience
wrapper),以某个类或对象的某个 public、protected 或 private 属性作为实际值来进
行比较。
例 A.44. assertSame() 的用法
<?php
use PHPUnitFrameworkTestCase;
class SameTest extends TestCase
{
public function testFailure()
{
$this->assertSame('2204', 2204);
}
}
?>
phpunit SameTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) SameTest::testFailure
Failed asserting that 2204 is identical to '2204'.
/home/sb/SameTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertSame(object $expected, object $actual[, string $message = ''])
当两个变量 $expected 和 $actual 不是指向同一个对象的引用时报告错误,错误讯息由
$message 指定。
例 A.45. assertSame() 应用于对象时的用法
<?php
use PHPUnitFrameworkTestCase;
133. 断言
123
class SameTest extends TestCase
{
public function testFailure()
{
$this->assertSame(new stdClass, new stdClass);
}
}
?>
phpunit SameTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 4.75Mb
There was 1 failure:
1) SameTest::testFailure
Failed asserting that two variables reference the same object.
/home/sb/SameTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertStringEndsWith()
assertStringEndsWith(string $suffix, string $string[, string
$message = ''])
当 $string 不以 $suffix 结尾时报告错误,错误讯息由 $message 指定。
assertStringEndsNotWith() 是与之相反的断言,接受相同的参数。
例 A.46. assertStringEndsWith() 的用法
<?php
use PHPUnitFrameworkTestCase;
class StringEndsWithTest extends TestCase
{
public function testFailure()
{
$this->assertStringEndsWith('suffix', 'foo');
}
}
?>
phpunit StringEndsWithTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 1 second, Memory: 5.00Mb
There was 1 failure:
1) StringEndsWithTest::testFailure
Failed asserting that 'foo' ends with "suffix".
134. 断言
124
/home/sb/StringEndsWithTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertStringEqualsFile()
assertStringEqualsFile(string $expectedFile, string $actualString[,
string $message = ''])
当 $expectedFile 所指定的文件其内容不是 $actualString 时报告错误,错误讯息由
$message 指定。
assertStringNotEqualsFile() 是与之相反的断言,接受相同的参数。
例 A.47. assertStringEqualsFile() 的用法
<?php
use PHPUnitFrameworkTestCase;
class StringEqualsFileTest extends TestCase
{
public function testFailure()
{
$this->assertStringEqualsFile('/home/sb/expected', 'actual');
}
}
?>
phpunit StringEqualsFileTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.25Mb
There was 1 failure:
1) StringEqualsFileTest::testFailure
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'expected
-'
+'actual'
/home/sb/StringEqualsFileTest.php:6
FAILURES!
Tests: 1, Assertions: 2, Failures: 1.
assertStringStartsWith()
assertStringStartsWith(string $prefix, string $string[, string
$message = ''])
当 $string 不以 $prefix 开头时报告错误,错误讯息由 $message 指定。
assertStringStartsNotWith() 是与之相反的断言,并接受相同的参数。
135. 断言
125
例 A.48. assertStringStartsWith() 的用法
<?php
use PHPUnitFrameworkTestCase;
class StringStartsWithTest extends TestCase
{
public function testFailure()
{
$this->assertStringStartsWith('prefix', 'foo');
}
}
?>
phpunit StringStartsWithTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.
F
Time: 0 seconds, Memory: 5.00Mb
There was 1 failure:
1) StringStartsWithTest::testFailure
Failed asserting that 'foo' starts with "prefix".
/home/sb/StringStartsWithTest.php:6
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
assertThat()
可以用 PHPUnit_Framework_Constraint 类来订立更加复杂的断言。随后可以用
assertThat() 方法来评定这些断言。例 A.49 “assertThat() 的用法” 展示了如何用
logicalNot() 和 equalTo() 约束条件来表达与 assertNotEquals() 等价的断言。
assertThat(mixed $value, PHPUnit_Framework_Constraint $constraint[,
$message = ''])
当 $value 不符合约束条件 $constraint 时报告错误,错误讯息由 $message 指定。
例 A.49. assertThat() 的用法
<?php
use PHPUnitFrameworkTestCase;
class BiscuitTest extends TestCase
{
public function testEquals()
{
$theBiscuit = new Biscuit('Ginger');
$myBiscuit = new Biscuit('Ginger');
$this->assertThat(
$theBiscuit,
$this->logicalNot(
$this->equalTo($myBiscuit)
)
);
}
164. 154
附录 F. 版权
Copyright (c) 2005-2017 Sebastian Bergmann.
此作品依照 Creative Commons Attribution 3.0
Unported License 授权。
以下是此授权许可协议的摘要信息,完整的法律文件附在其后。
--------------------------------------------------------------------
您可以自由地:
* 分享 - 复制、分发、传播此作品
* 重组 - 创作演绎此作品
惟须遵守下列条件:
姓名标示。弄必须按照作者或者版权人指定的方式表彰其姓名(但不得以任何方式暗示他们认可你或你使用本作品的方式
* 在再使用或者发行本作品时,您必须向他人明示本作品使用的许可协议条款。明示的最佳方法是附上本网页的链接
* 若您获得著作权人准许,则上述所有条件都可予以免除。
* 此协议对作者的人身权不构成任何损害与限制。
合理使用及其他权利不受许可协议影响。
以上是易于常人了解的法律条文(完整的授权许可协议)摘要。
====================================================================
Creative Commons Legal Code
Attribution 3.0 Unported
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO
WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS
LIABILITY FOR DAMAGES RESULTING FROM ITS USE.
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW
IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF
SUCH TERMS AND CONDITIONS.
1. Definitions
a. "Adaptation" means a work based upon the Work, or upon the
Work and other pre-existing works, such as a translation,
adaptation, derivative work, arrangement of music or other
alterations of a literary or artistic work, or phonogram or
165. 版权
155
performance and includes cinematographic adaptations or any
other form in which the Work may be recast, transformed, or
adapted including in any form recognizably derived from the
original, except that a work that constitutes a Collection
will not be considered an Adaptation for the purpose of this
License. For the avoidance of doubt, where the Work is a
musical work, performance or phonogram, the synchronization of
the Work in timed-relation with a moving image ("synching")
will be considered an Adaptation for the purpose of this
License.
b. "Collection" means a collection of literary or artistic works,
such as encyclopedias and anthologies, or performances,
phonograms or broadcasts, or other works or subject matter
other than works listed in Section 1(f) below, which, by
reason of the selection and arrangement of their contents,
constitute intellectual creations, in which the Work is
included in its entirety in unmodified form along with one or
more other contributions, each constituting separate and
independent works in themselves, which together are assembled
into a collective whole. A work that constitutes a Collection
will not be considered an Adaptation (as defined above) for
the purposes of this License.
c. "Distribute" means to make available to the public the
original and copies of the Work or Adaptation, as appropriate,
through sale or other transfer of ownership.
d. "Licensor" means the individual, individuals, entity or
entities that offer(s) the Work under the terms of this License.
e. "Original Author" means, in the case of a literary or artistic
work, the individual, individuals, entity or entities who
created the Work or if no individual or entity can be
identified, the publisher; and in addition (i) in the case of
a performance the actors, singers, musicians, dancers, and
other persons who act, sing, deliver, declaim, play in,
interpret or otherwise perform literary or artistic works or
expressions of folklore; (ii) in the case of a phonogram the
producer being the person or legal entity who first fixes the
sounds of a performance or other sounds; and, (iii) in the
case of broadcasts, the organization that transmits the
broadcast.
f. "Work" means the literary and/or artistic work offered under
the terms of this License including without limitation any
production in the literary, scientific and artistic domain,
whatever may be the mode or form of its expression including
digital form, such as a book, pamphlet and other writing; a
lecture, address, sermon or other work of the same nature; a
dramatic or dramatico-musical work; a choreographic work or
entertainment in dumb show; a musical composition with or
without words; a cinematographic work to which are assimilated
works expressed by a process analogous to cinematography; a
work of drawing, painting, architecture, sculpture, engraving
or lithography; a photographic work to which are assimilated
works expressed by a process analogous to photography; a work
of applied art; an illustration, map, plan, sketch or three-
dimensional work relative to geography, topography,
architecture or science; a performance; a broadcast; a
phonogram; a compilation of data to the extent it is protected
as a copyrightable work; or a work performed by a variety or
circus performer to the extent it is not otherwise considered
a literary or artistic work.
166. 版权
156
g. "You" means an individual or entity exercising rights under
this License who has not previously violated the terms of
this License with respect to the Work, or who has received
express permission from the Licensor to exercise rights under
this License despite a previous violation.
h. "Publicly Perform" means to perform public recitations of the
Work and to communicate to the public those public
recitations, by any means or process, including by wire or
wireless means or public digital performances; to make
available to the public Works in such a way that members of
the public may access these Works from a place and at a place
individually chosen by them; to perform the Work to the public
by any means or process and the communication to the public of
the performances of the Work, including by public digital
performance; to broadcast and rebroadcast the Work by any
means including signs, sounds or images.
i. "Reproduce" means to make copies of the Work by any means
including without limitation by sound or visual recordings and
the right of fixation and reproducing fixations of the Work,
including storage of a protected performance or phonogram in
digital form or other electronic medium.
2. Fair Dealing Rights. Nothing in this License is intended to
reduce, limit, or restrict any uses free from copyright or rights
arising from limitations or exceptions that are provided for in
connection with the copyright protection under copyright law or
other applicable laws.
3. License Grant. Subject to the terms and conditions of this
License, Licensor hereby grants You a worldwide, royalty-free,
non-exclusive, perpetual (for the duration of the applicable
copyright) license to exercise the rights in the Work as stated
below:
a. to Reproduce the Work, to incorporate the Work into one or
more Collections, and to Reproduce the Work as incorporated
in the Collections;
b. to create and Reproduce Adaptations provided that any such
Adaptation, including any translation in any medium, takes
reasonable steps to clearly label, demarcate or otherwise
identify that changes were made to the original Work. For
example, a translation could be marked "The original work was
translated from English to Spanish," or a modification could
indicate "The original work has been modified.";
c. to Distribute and Publicly Perform the Work including as
incorporated in Collections; and,
d. to Distribute and Publicly Perform Adaptations.
e. For the avoidance of doubt:
i. Non-waivable Compulsory License Schemes. In those
jurisdictions in which the right to collect royalties
through any statutory or compulsory licensing scheme cannot
be waived, the Licensor reserves the exclusive right to
collect such royalties for any exercise by You of the
rights granted under this License;
ii. Waivable Compulsory License Schemes. In those
167. 版权
157
jurisdictions in which the right to collect royalties
through any statutory or compulsory licensing scheme can
be waived, the Licensor waives the exclusive right to
collect such royalties for any exercise by You of the
rights granted under this License; and,
iii. Voluntary License Schemes. The Licensor waives the right
to collect royalties, whether individually or, in the
event that the Licensor is a member of a collecting
society that administers voluntary licensing schemes, via
that society, from any exercise by You of the rights
granted under this License.
The above rights may be exercised in all media and formats whether
now known or hereafter devised. The above rights include the right
to make such modifications as are technically necessary to exercise
the rights in other media and formats. Subject to Section 8(f), all
rights not expressly granted by Licensor are hereby reserved.
4. Restrictions. The license granted in Section 3 above is expressly
made subject to and limited by the following restrictions:
a. You may Distribute or Publicly Perform the Work only under the
terms of this License. You must include a copy of, or the
Uniform Resource Identifier (URI) for, this License with every
copy of the Work You Distribute or Publicly Perform. You may
not offer or impose any terms on the Work that restrict the
terms of this License or the ability of the recipient of the
Work to exercise the rights granted to that recipient under
the terms of the License. You may not sublicense the Work. You
must keep intact all notices that refer to this License and to
the disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or
Publicly Perform the Work, You may not impose any effective
technological measures on the Work that restrict the ability
of a recipient of the Work from You to exercise the rights
granted to that recipient under the terms of the License. This
Section 4(a) applies to the Work as incorporated in a
Collection, but this does not require the Collection apart
from the Work itself to be made subject to the terms of this
License. If You create a Collection, upon notice from any
Licensor You must, to the extent practicable, remove from the
Collection any credit as required by Section 4(b), as
requested. If You create an Adaptation, upon notice from any
Licensor You must, to the extent practicable, remove from the
Adaptation any credit as required by Section 4(b), as requested.
b. If You Distribute, or Publicly Perform the Work or any
Adaptations or Collections, You must, unless a request has
been made pursuant to Section 4(a), keep intact all copyright
notices for the Work and provide, reasonable to the medium or
means You are utilizing: (i) the name of the Original Author
(or pseudonym, if applicable) if supplied, and/or if the
Original Author and/or Licensor designate another party or
parties (e.g., a sponsor institute, publishing entity,
journal) for attribution ("Attribution Parties") in Licensor's
copyright notice, terms of service or by other reasonable
means, the name of such party or parties; (ii) the title of
the Work if supplied; (iii) to the extent reasonably
practicable, the URI, if any, that Licensor specifies to be
associated with the Work, unless such URI does not refer to
the copyright notice or licensing information for the Work;
and (iv), consistent with Section 3(b), in the case of an
Adaptation, a credit identifying the use of the Work in the
168. 版权
158
Adaptation (e.g., "French translation of the Work by Original
Author," or "Screenplay based on original Work by Original
Author"). The credit required by this Section 4 (b) may be
implemented in any reasonable manner; provided, however, that
in the case of a Adaptation or Collection, at a minimum such
credit will appear, if a credit for all contributing authors
of the Adaptation or Collection appears, then as part of these
credits and in a manner at least as prominent as the credits
for the other contributing authors. For the avoidance of
doubt, You may only use the credit required by this Section
for the purpose of attribution in the manner set out above
and, by exercising Your rights under this License, You may not
implicitly or explicitly assert or imply any connection with,
sponsorship or endorsement by the Original Author, Licensor
and/or Attribution Parties, as appropriate, of You or Your use
of the Work, without the separate, express prior written
permission of the Original Author, Licensor and/or
Attribution Parties.
c. Except as otherwise agreed in writing by the Licensor or as
may be otherwise permitted by applicable law, if You
Reproduce, Distribute or Publicly Perform the Work either by
itself or as part of any Adaptations or Collections, You must
not distort, mutilate, modify or take other derogatory action
in relation to the Work which would be prejudicial to the
Original Author's honor or reputation. Licensor agrees that in
those jurisdictions (e.g. Japan), in which any exercise of the
right granted in Section 3(b) of this License (the right to
make Adaptations) would be deemed to be a distortion,
mutilation, modification or other derogatory action
prejudicial to the Original Author's honor and reputation, the
Licensor will waive or not assert, as appropriate, this
Section, to the fullest extent permitted by the applicable
national law, to enable You to reasonably exercise Your right
under Section 3(b) of this License (right to make Adaptations)
but not otherwise.
5. Representations, Warranties and Disclaimer
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF
IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF
THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY
OF SUCH DAMAGES.
7. Termination
a. This License and the rights granted hereunder will terminate
automatically upon any breach by You of the terms of this
License. Individuals or entities who have received Adaptations
or Collections from You under this License, however, will not
have their licenses terminated provided such individuals or
entities remain in full compliance with those licenses.
169. 版权
159
Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
this License.
b. Subject to the above terms and conditions, the license granted
here is perpetual (for the duration of the applicable
copyright in the Work). Notwithstanding the above, Licensor
reserves the right to release the Work under different license
terms or to stop distributing the Work at any time; provided,
however that any such election will not serve to withdraw this
License (or any other license that has been, or is required to
be, granted under the terms of this License), and this License
will continue in full force and effect unless terminated as
stated above.
8. Miscellaneous
a. Each time You Distribute or Publicly Perform the Work or a
Collection, the Licensor offers to the recipient a license to
the Work on the same terms and conditions as the license
granted to You under this License.
b. Each time You Distribute or Publicly Perform an Adaptation,
Licensor offers to the recipient a license to the original
Work on the same terms and conditions as the license granted
to You under this License.
c. If any provision of this License is invalid or unenforceable
under applicable law, it shall not affect the validity or
enforceability of the remainder of the terms of this License,
and without further action by the parties to this agreement,
such provision shall be reformed to the minimum extent
necessary to make such provision valid and enforceable.
d. No term or provision of this License shall be deemed waived
and no breach consented to unless such waiver or consent shall
be in writing and signed by the party to be charged with such
waiver or consent.
e. This License constitutes the entire agreement between the
parties with respect to the Work licensed here. There are no
understandings, agreements or representations with respect to
the Work not specified here. Licensor shall not be bound by
any additional provisions that may appear in any communication
from You. This License may not be modified without the mutual
written agreement of the Licensor and You.
f. The rights granted under, and the subject matter referenced,
in this License were drafted utilizing the terminology of the
Berne Convention for the Protection of Literary and Artistic
Works (as amended on September 28, 1979), the Rome Convention
of 1961, the WIPO Copyright Treaty of 1996, the WIPO
Performances and Phonograms Treaty of 1996 and the Universal
Copyright Convention (as revised on July 24, 1971). These
rights and subject matter take effect in the relevant
jurisdiction in which the License terms are sought to be
enforced according to the corresponding provisions of the
implementation of those treaty provisions in the applicable
national law. If the standard suite of rights granted under
applicable copyright law includes additional rights not
granted under this License, such additional rights are deemed
to be included in the License; this License is not intended to
restrict the license of any rights under applicable law.
Creative Commons is not a party to this License, and makes no
170. 版权
160
warranty whatsoever in connection with the Work. Creative Commons
will not be liable to You or any party on any legal theory for any
damages whatsoever, including without limitation any general,
special, incidental or consequential damages arising in connection
to this license. Notwithstanding the foregoing two (2) sentences,
if Creative Commons has expressly identified itself as the Licensor
hereunder, it shall have all rights and obligations of Licensor.
Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of
doubt, this trademark restriction does not form part of this
License.
Creative Commons may be contacted at http://creativecommons.org/.
====================================================================