测试PHP代码的主要手段是使用PHPUnit,它是基于一种叫做单元测试的方法论。单元测试背后的理念非常简单:将代码分解成尽可能小的逻辑单元。然后对每个单元进行隔离测试,以确认它的性能符合预期。这些预期被编纂成一系列的断言。如果所有的断言都返回 "true",那么这个单元就通过了测试。
在程序化PHP中,单元是一个函数。对于OOP PHP来说,单元是一个类中的方法。
如何做...
1.首先要做的是直接将 PHPUnit 安装到你的开发服务器上,或者下载源码,源码以单个 phar(PHP 档案)文件的形式存在。快速访问PHPUnit的官方网站(https://phpunit.de/),我们可以直接从主页下载。
2. 然而,最好的做法是使用一个包管理器来安装和维护 PHPUnit。为此,我们将使用一个名为 Composer 的软件包管理程序。要安装 Composer,请访问主网站 https://getcomposer.org/,并按照下载页面的说明进行安装。目前的程序,在写这篇文章的时候,如下所示。注意,你需要用当前版本的哈希值代替<hash>
。
复制 php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === '<hash>') {
echo 'Installer verified';
} else {
echo 'Installer corrupt'; unlink('composer-setup.php');
} echo PHP_EOL;"
php composer-setup.php
php -r "unlink('composer-setup.php');"
最佳实践
使用Composer这样的软件包管理程序的好处是,它不仅可以安装,还可以用来更新你的应用程序所使用的任何外部软件(如PHPUnit)。
3. 接下来,我们使用Composer来安装PHPUnit。这是通过创建一个composer.json
文件来完成的,该文件包含一系列概述项目参数和依赖关系的指令。对这些指令的完整描述超出了本书的范围;然而,为了本实例的目的,我们使用关键参数 require
创建了一组最小的指令。你还会注意到,文件的内容是JavaScript对象符号(JSON) 格式。
复制 {
"require-dev" : {
"phpunit/phpunit" : "*"
}
}
4. 要从命令行进行安装,我们运行以下命令。后面的输出就会显示出来。
复制 php composer.phar install
5. PHPUnit 和它的依赖项被放置在一个 vendor
文件夹中,如果该文件夹不存在,Composer 将会创建它。然后,调用 PHPUnit 的主要命令被符号化地链接到 vendor/bin
文件夹中。如果你把这个文件夹放在你的 PATH
中,你所需要做的就是运行这个命令,它将检查版本并顺便确认安装。
运行简单的测试
1.为了便于说明,我们假设我们有一个包含add()
函数的chap_13_unit_test_simple.php
文件。
复制 <? php
function add ($a = NULL , $b = NULL )
{
return $a + $b;
}
2. 然后将测试写成扩展PHPUnit\Framework\TestCase
的类。如果你要测试一个函数库,在测试类的开头,包括包含函数定义的文件。然后,你会写出以test
开头的方法,通常后面是你要测试的函数的名称,可能还有一些额外的CamelCase
词来进一步描述测试。在本示例中,我们将定义一个SimpleTest
测试类。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/chap_13_unit_test_simple.php' ;
class SimpleTest extends TestCase
{
// testXXX() methods go here
}
3. 断言是任何测试集的核心。一个断言是一个PHPUnit方法,它将一个已知的值和你想测试的值进行比较。一个例子是 assertEquals()
,它检查第一个参数是否等于第二个参数。下面的例子测试了一个名为 add()
的方法,并确认 2 是 add(1,1)
的返回值。
复制 public function testAdd ()
{
$this -> assertEquals ( 2 , add ( 1 , 1 )) ;
}
4. 你也可以测试一下某件事情是否不真实。这个例子断言1+1不等于3。
复制 $this -> assertNotEquals ( 3 , add ( 1 , 1 )) ;
5. assertRegExp()
是一个在测试字符串时非常有用的断言。在这个例子中,假设我们正在测试一个从一个多维数组中生成一个HTML表格的函数。
复制 function table ( array $a)
{
$table = '<table>' ;
foreach ($a as $row) {
$table .= '<tr><td>' ;
$table .= implode ( '</td><td>' , $row ) ;
$table .= '</td></tr>' ;
}
$table .= '</table>' ;
return $table;
}
6. 我们可以构造一个简单的测试,以确认输出包含<table>
,一个或多个字符,然后是</table>
。此外,我们希望确认元素<td>B</td>
存在。在编写测试时,我们建立一个由三个子数组组成的测试数组,其中包含字母A-C、D-F和G-I。然后我们将测试数组传递给函数,并针对结果运行断言。
复制 public function testTable ()
{
$a = [ range ( 'A' , 'C' ), range ( 'D' , 'F' ), range ( 'G' , 'I' ) ];
$table = table ( $a ) ;
$this -> assertRegExp ( '!^<table>.+</table>$!' , $table ) ;
$this -> assertRegExp ( '!<td>B</td>!' , $table ) ;
}
7. 要测试一个类,不需要包含一个函数库,只需要包含定义要测试的类的文件。为了便于说明,让我们把前面显示的函数库移到Demo类中。
复制 <? php
class Demo
{
public function add ($a , $b)
{
return $a + $b;
}
public function sub ($a , $b)
{
return $a - $b;
}
// etc.
}
8. 在我们的SimpleClassTest
测试类中,我们不包含库文件,而是包含代表Demo
类的文件。为了运行测试,我们需要一个Demo
的实例。为此,我们使用了一个专门设计的setup()
方法,它在每次测试之前都会运行。另外,你会注意到一个 teardown()
方法,它是在每次测试后立即运行的。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/Demo.php' ;
class SimpleClassTest extends TestCase
{
protected $demo;
public function setup ()
{
$this -> demo = new Demo ();
}
public function teardown ()
{
unset ( $this -> demo ) ;
}
public function testAdd ()
{
$this -> assertEquals ( 2 , $this -> demo -> add ( 1 , 1 )) ;
}
public function testSub ()
{
$this -> assertEquals ( 0 , $this -> demo -> sub ( 1 , 1 )) ;
}
// etc.
}
之所以在每次测试前后运行setup()
和trapdown()
,是为了保证一个新鲜的测试环境。这样,一个测试的结果就不会影响另一个测试的结果。
测试数据库模型类
1.当测试一个有数据库访问权限的类(如Model类)时,其他的考虑因素也在发挥作用。主要的考虑是,你应该针对测试数据库,而不是生产中使用的真实数据库来运行测试。最后一点是,通过使用测试数据库,你可以事先用适当的、受控的数据填充它,setup()
和 teardown()
也可以用来添加或删除测试数据。
2. 作为一个使用数据库的类的例子,我们将定义一个类 VisitorOps
。这个新类将包括添加、删除和查找访客的方法。请注意,我们还添加了一个方法来返回最新执行的SQL语句。
复制 <? php
require __DIR__ . '/../Application/Database/Connection.php' ;
use Application \ Database \ Connection ;
class VisitorOps
{
const TABLE_NAME = 'visitors' ;
protected $connection;
protected $sql;
public function __construct ( array $config)
{
$this -> connection = new Connection ($config);
}
public function getSql ()
{
return $this -> sql;
}
public function findAll ()
{
$sql = 'SELECT * FROM ' . self:: TABLE_NAME;
$stmt = $this -> runSql ( $sql ) ;
while ($row = $stmt -> fetch ( PDO :: FETCH_ASSOC ) ) {
yield $row;
}
}
public function findById ($id)
{
$sql = 'SELECT * FROM ' . self:: TABLE_NAME;
$sql .= ' WHERE id = ?' ;
$stmt = $this -> runSql ( $sql , [$id] ) ;
return $stmt -> fetch ( PDO :: FETCH_ASSOC ) ;
}
public function removeById ($id)
{
$sql = 'DELETE FROM ' . self:: TABLE_NAME;
$sql .= ' WHERE id = ?' ;
return $this -> runSql ( $sql , [$id] ) ;
}
public function addVisitor ($data)
{
$sql = 'INSERT INTO ' . self:: TABLE_NAME;
$sql .= ' (' . implode ( ',' , array_keys ( $data )) . ') ' ;
$sql .= ' VALUES ' ;
$sql .= ' ( :' . implode ( ',:' , array_keys ( $data )) . ') ' ;
$this -> runSql ( $sql , $data ) ;
return $this -> connection -> pdo -> lastInsertId () ;
}
public function runSql ($sql , $params = NULL )
{
$this -> sql = $sql;
try {
$stmt = $this -> connection -> pdo -> prepare ( $sql ) ;
$result = $stmt -> execute ( $params ) ;
} catch ( Throwable $e) {
error_log ( __METHOD__ . ':' . $e -> getMessage ()) ;
return FALSE ;
}
return $stmt;
}
}
3. 对于涉及数据库的测试,建议使用测试数据库而不是实时生产数据库。相应地,你将需要一组额外的数据库连接参数,可以用来在setup()
方法中建立数据库连接。
4. 有可能你希望建立一个一致的样本数据块。这可以在setup()
方法中插入到测试数据库中。
5. 最后,你可能希望在每次测试后重置测试数据库,这在 teardown()
方法中完成。
使用MOCK类
1.在某些情况下,测试将访问需要外部资源的复杂组件。一个例子是需要访问数据库的服务类。在测试套件中尽量减少数据库访问是一个最佳实践。另一个考虑因素是,我们不是在测试数据库访问;我们只是在测试一个特定类的功能。因此,有时有必要定义模拟类,模仿其父类的行为,但限制对外部资源的访问。
最佳实践
在你的测试中,将实际的数据库访问限制在Model(或同等的)类中。否则,运行整套测试所需的时间可能会过长。
2. 在这种情况下,为了说明问题,定义一个服务类VisitorService
,它使用了前面讨论的VisitorOps
类。
复制 <? php
require_once __DIR__ . '/VisitorOps.php' ;
require_once __DIR__ . '/../Application/Database/Connection.php' ;
use Application \ Database \ Connection ;
class VisitorService
{
protected $visitorOps;
public function __construct ( array $config)
{
$this -> visitorOps = new VisitorOps ($config);
}
public function showAllVisitors ()
{
$table = '<table>' ;
foreach ( $this -> visitorOps -> findAll () as $row) {
$table .= '<tr><td>' ;
$table .= implode ( '</td><td>' , $row ) ;
$table .= '</td></tr>' ;
}
$table .= '</table>' ;
return $table;
}
3. 为了测试的目的,我们为$visitorOps
属性添加一个getter和setter。这允许我们插入一个模拟类来代替真正的VisitorOps
类。
复制 public function getVisitorOps ()
{
return $this -> visitorOps;
}
public function setVisitorOps ( VisitorOps $visitorOps)
{
$this -> visitorOps = $visitorOps;
}
} // closing brace for VisitorService
4. 接下来,我们定义一个VisitorOpsMock
模拟类,模仿其父类的功能。类的常量和属性都是继承的。然后我们添加模拟测试数据,以及一个getter,以备以后需要访问测试数据时使用。
复制 <? php
require_once __DIR__ . '/VisitorOps.php' ;
class VisitorOpsMock extends VisitorOps
{
protected $testData;
public function __construct ()
{
$data = array ();
for ($x = 1 ; $x <= 3 ; $x ++ ) {
$data[$x][ 'id' ] = $x;
$data[$x][ 'email' ] = $x . 'test@unlikelysource.com' ;
$data[$x][ 'visit_date' ] =
'2000-0' . $x . '-0' . $x . ' 00:00:00' ;
$data[$x][ 'comments' ] = 'TEST ' . $x;
$data[$x][ 'name' ] = 'TEST ' . $x;
}
$this -> testData = $data;
}
public function getTestData ()
{
return $this -> testData;
}
5. 接下来,我们重写findAll()
来使用yield
返回测试数据,就像在父类中一样。请注意,我们仍然构建SQL字符串,因为这是父类的工作。
复制 public function findAll ()
{
$sql = 'SELECT * FROM ' . self:: TABLE_NAME;
foreach ( $this -> testData as $row) {
yield $row;
}
}
6. 为了模拟findById()
,我们简单地从$this->testData
返回数组键。对于removeById()
,我们取消设置从$this->testData
中提供的数组键作为参数。
复制 public function findById ($id)
{
$sql = 'SELECT * FROM ' . self:: TABLE_NAME;
$sql .= ' WHERE id = ?' ;
return $this -> testData[$id] ?? FALSE ;
}
public function removeById ($id)
{
$sql = 'DELETE FROM ' . self:: TABLE_NAME;
$sql .= ' WHERE id = ?' ;
if ( empty ( $this -> testData[$id] ) ) {
return 0 ;
} else {
unset ( $this -> testData[$id] ) ;
return 1 ;
}
}
7. 添加数据稍微复杂一些,因为我们需要模拟一个事实,即id
参数可能没有被提供,因为数据库通常会自动为我们生成这个参数。为了解决这个问题,我们检查id
参数。如果没有设置,我们找到最大的数组键,然后递增。
复制 public function addVisitor ($data)
{
$sql = 'INSERT INTO ' . self:: TABLE_NAME;
$sql .= ' (' . implode ( ',' , array_keys ( $data )) . ') ' ;
$sql .= ' VALUES ' ;
$sql .= ' ( :' . implode ( ',:' , array_keys ( $data )) . ') ' ;
if ( ! empty ( $data[ 'id' ] ) ) {
$id = $data[ 'id' ];
} else {
$keys = array_keys ( $this -> testData ) ;
sort ( $keys ) ;
$id = end ( $keys ) + 1 ;
$data[ 'id' ] = $id;
}
$this -> testData[$id] = $data;
return 1 ;
}
} // ending brace for the class VisitorOpsMock
使用匿名类作为模拟对象
1.关于mock对象的一个很好的变化是使用新的PHP 7匿名类来代替创建一个定义mock功能的正式类。使用匿名类的好处是可以扩展一个现有的类,这使得对象看起来合法。如果你只需要覆盖一两个方法,这种方法特别有用。
2. 在这个例子中,我们将修改之前介绍的VisitorServiceTest.php
,将其称为VisitorServiceTestAnonClass.php
。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/VisitorService.php' ;
require_once __DIR__ . '/VisitorOps.php' ;
class VisitorServiceTestAnonClass extends TestCase
{
protected $visitorService;
protected $dbConfig = [
'driver' => 'mysql' ,
'host' => 'localhost' ,
'dbname' => 'php7cookbook_test' ,
'user' => 'cook' ,
'password' => 'book' ,
'errmode' => PDO :: ERRMODE_EXCEPTION ,
];
protected $testData;
3. 您会注意到,在setup()
中,我们定义了一个匿名类,该类扩展了VisitorOps
。我们只需要重写findAll()
方法。
复制 public function setup ()
{
$data = array ();
for ($x = 1 ; $x <= 3 ; $x ++ ) {
$data[$x][ 'id' ] = $x;
$data[$x][ 'email' ] = $x . 'test@unlikelysource.com' ;
$data[$x][ 'visit_date' ] =
'2000-0' . $x . '-0' . $x . ' 00:00:00' ;
$data[$x][ 'comments' ] = 'TEST ' . $x;
$data[$x][ 'name' ] = 'TEST ' . $x;
}
$this -> testData = $data;
$this -> visitorService =
new VisitorService ( $this -> dbConfig);
$opsMock =
new class ( $this -> testData ) extends VisitorOps {
protected $testData;
public function __construct ($testData)
{
$this -> testData = $testData;
}
public function findAll ()
{
return $this -> testData;
}
};
$this -> visitorService -> setVisitorOps ( $opsMock ) ;
}
4. 请注意,在testShowAllVisitors()
中,当$this->visitorService->showAllVisitors()
被执行时,匿名类会被访问者服务调用,而访问者服务又会调用重写的findAll()
。
复制 public function teardown ()
{
unset ( $this -> visitorService ) ;
}
public function testShowAllVisitors ()
{
$result = $this -> visitorService -> showAllVisitors () ;
$this -> assertRegExp ( '!^<table>.+</table>$!' , $result ) ;
foreach ( $this -> testData as $key => $value) {
$dataWeWant = '!<td>' . $key . '</td>!' ;
$this -> assertRegExp ( $dataWeWant , $result ) ;
}
}
}
使用MOCK BUILDER
1.另一种技术是使用getMockBuilder()
。虽然这种方法不允许对产生的mock对象进行大量的有限控制,但在你只需要确认返回某个类的对象,并且当运行指定的方法时,这个方法会返回一些预期的值的情况下,这种方法是非常有用的。
2. 在下面的示例中,我们复制了VisitorServiceTestAnonClass
;唯一的区别在于如何在setup()
中提供VisitorOps
的实例,在本例中,使用getMockBuilder()
。请注意,虽然我们在这个例子中没有使用with()
,但它是用来将受控参数馈送到模拟方法的。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/VisitorService.php' ;
require_once __DIR__ . '/VisitorOps.php' ;
class VisitorServiceTestAnonMockBuilder extends TestCase
{
// code is identical to VisitorServiceTestAnon
public function setup ()
{
$data = array ();
for ($x = 1 ; $x <= 3 ; $x ++ ) {
$data[$x][ 'id' ] = $x;
$data[$x][ 'email' ] = $x . 'test@unlikelysource.com' ;
$data[$x][ 'visit_date' ] =
'2000-0' . $x . '-0' . $x . ' 00:00:00' ;
$data[$x][ 'comments' ] = 'TEST ' . $x;
$data[$x][ 'name' ] = 'TEST ' . $x;
}
$this -> testData = $data;
$this -> visitorService =
new VisitorService ( $this -> dbConfig);
$opsMock = $this -> getMockBuilder ( VisitorOps ::class )
-> setMethods ( [ 'findAll' ] )
-> disableOriginalConstructor ()
-> getMock () ;
$opsMock -> expects ( $this -> once ())
-> method ( 'findAll' )
-> with ()
-> will ( $this -> returnValue ( $this -> testData )) ;
$this -> visitorService -> setVisitorOps ( $opsMock ) ;
}
// remaining code is the same
}
我们已经展示了如何创建简单的一次性测试。然而,在大多数情况下,你会有许多需要测试的类,最好是一次全部测试。这可以通过开发一个测试套件来实现,在下一个事例中会有更详细的讨论。
如何运行...
首先,你需要安装 PHPUnit,如步骤 1 至 5 所述。 确保在 PATH 中包含 vendor/bin
,这样你就可以从命令行运行 PHPUnit。
运行简单的测试
接下来,定义一个chap_13_unit_test_simple.php
程序文件,其中包含一系列简单的函数,如步骤1中讨论的add()
、sub()
等。然后你可以定义一个简单的测试类,包含在SimpleTest.php
中,如步骤2和步骤3中提到的。
假设phpunit
在你的PATH
中,从终端窗口,改变到包含为这个配方开发的代码的目录,并运行以下命令。
复制 phpunit SimpleTest SimpleTest.php
你应该看到以下输出。
在SimpleTest.php
中进行修改,使测试失败(第4步)。
复制 public function testDiv ()
{
$this -> assertEquals ( 2 , div ( 4 , 2 )) ;
$this -> assertEquals ( 99 , div ( 4 , 0 )) ;
}
这是修订后的产出。
接下来,在chap_13_unit_test_simple.php
中添加table()
函数(步骤5),在SimpleTest.php
中添加testTable()
(步骤6)。重新运行单元测试,观察结果。
要测试一个类,将在chap_13_unit_test_simple.php
中开发的函数复制到Demo类中(步骤7)。在对步骤8中建议的SimpleTest.php
进行修改后,重新运行简单测试并观察结果。
测试数据库模型类
首先,创建一个要测试的示例类,VisitorOps
,如本小节步骤2所示。现在你可以定义一个类,我们将调用SimpleDatabaseTest
来测试VisitorOps
。首先,使用require_once
来加载要测试的类。(我们将在下一个实例中讨论如何加入自动加载!)然后定义关键属性,包括测试数据库配置和测试数据。你可以使用php7cookbook_test
作为测试数据库。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/VisitorOps.php' ;
class SimpleDatabaseTest extends TestCase
{
protected $visitorOps;
protected $dbConfig = [
'driver' => 'mysql' ,
'host' => 'localhost' ,
'dbname' => 'php7cookbook_test' ,
'user' => 'cook' ,
'password' => 'book' ,
'errmode' => PDO :: ERRMODE_EXCEPTION ,
];
protected $testData = [
'id' => 1 ,
'email' => 'test@unlikelysource.com' ,
'visit_date' => '2000-01-01 00:00:00' ,
'comments' => 'TEST' ,
'name' => 'TEST'
];
}
接下来,定义setup()
,插入测试数据,并确认最后一条SQL语句是INSERT
。还应该检查返回值是否为正值。
复制 public function setup ()
{
$this -> visitorOps = new VisitorOps ( $this -> dbConfig);
$this -> visitorOps -> addVisitor ( $this -> testData ) ;
$this -> assertRegExp ( '/INSERT/' , $this -> visitorOps -> getSql ()) ;
}
之后,定义 teardown()
,删除测试数据,并确认查询 id = 1
的结果为 FALSE
。
复制 public function teardown ()
{
$result = $this -> visitorOps -> removeById ( 1 ) ;
$result = $this -> visitorOps -> findById ( 1 ) ;
$this -> assertEquals ( FALSE , $result ) ;
unset ( $this -> visitorOps ) ;
}
首先测试的是findAll()
。首先,确认结果的数据类型。你可以用current()
取最上面的元素。我们确认有五个元素,其中有一个是name
,并且其值与测试数据中的值相同。
复制 public function testFindAll ()
{
$result = $this -> visitorOps -> findAll () ;
$this -> assertInstanceOf ( Generator ::class , $result ) ;
$top = $result -> current () ;
$this -> assertCount ( 5 , $top ) ;
$this -> assertArrayHasKey ( 'name' , $top ) ;
$this -> assertEquals ( $this -> testData[ 'name' ] , $top[ 'name' ] ) ;
下一个测试是针对findById()
。它与testFindAll()
几乎相同。
复制 public function testFindById ()
{
$result = $this -> visitorOps -> findById ( 1 ) ;
$this -> assertCount ( 5 , $result ) ;
$this -> assertArrayHasKey ( 'name' , $result ) ;
$this -> assertEquals ( $this -> testData[ 'name' ] , $result[ 'name' ] ) ;
}
你不需要费心去测试 removeById()
,因为这已经在 teardown()
中完成了。同样,也不需要测试runSql()
,因为这已经作为其他测试的一部分完成了。
使用MOCK类
首先,定义一个 VisitorService
服务类,如本小节步骤 2 和 3 所述。接下来,定义一个VisitorOpsMock
模拟类,这将在步骤4至7中讨论。
您现在可以为服务类开发一个测试,即 VisitorServiceTest
。请注意,您需要提供您自己的数据库配置,因为最好的做法是使用测试数据库而不是生产版本。
复制 <? php
use PHPUnit \ Framework \ TestCase ;
require_once __DIR__ . '/VisitorService.php' ;
require_once __DIR__ . '/VisitorOpsMock.php' ;
class VisitorServiceTest extends TestCase
{
protected $visitorService;
protected $dbConfig = [
'driver' => 'mysql' ,
'host' => 'localhost' ,
'dbname' => 'php7cookbook_test' ,
'user' => 'cook' ,
'password' => 'book' ,
'errmode' => PDO :: ERRMODE_EXCEPTION ,
];
}
在setup()
中,创建一个服务的实例,并在原类的位置插入VisitorOpsMock
。
复制 public function setup ()
{
$this -> visitorService = new VisitorService ( $this -> dbConfig);
$this -> visitorService -> setVisitorOps ( new VisitorOpsMock () ) ;
}
public function teardown ()
{
unset ( $this -> visitorService ) ;
}
在我们的测试中,从访问者列表中产生一个HTML表格,然后你可以寻找某些元素,提前知道会发生什么,因为你可以控制测试数据。
复制 public function testShowAllVisitors ()
{
$result = $this -> visitorService -> showAllVisitors () ;
$this -> assertRegExp ( '!^<table>.+</table>$!' , $result ) ;
$testData = $this -> visitorService -> getVisitorOps () -> getTestData () ;
foreach ($testData as $key => $value) {
$dataWeWant = '!<td>' . $key . '</td>!' ;
$this -> assertRegExp ( $dataWeWant , $result ) ;
}
}
}
然后,你可能会希望尝试最后两个小节中建议的变化,使用匿名类作为Mock对象,以及使用Mock Builder。
更多...
其他断言测试对数字、字符串、数组、对象、文件、JSON和XML的操作,如下表所示。
assertEquals()
, assertFalse()
, assertEmpty()
, assertNull()
, assertSame(), assertThat()
, assertTrue()
assertGreaterThan()
, assertGreaterThanOrEqual()
, assertLessThan()
, assertLessThanOrEqual()
, assertNan()
, assertInfinite()
assertStringEndsWith()
, assertStringEqualsFile()
, assertStringStartsWith()
, assertRegExp()
, assertStringMatchesFormat()
, assertStringMatchesFormatFile()
assertArrayHasKey()
, assertArraySubset()
, assertContains()
, assertContainsOnly()
, assertContainsOnlyInstancesOf()
, assertCount()
assertFileEquals()
, assertFileExists()
assertClassHasAttribute()
, assertClassHasStaticAttribute()
, assertInstanceOf()
, assertInternalType()
, assertObjectHasAttribute()
assertJsonFileEqualsJsonFile()
, assertJsonStringEqualsJsonFile()
, assertJsonStringEqualsJsonString()
assertEqualXMLStructure()
, assertXmlFileEqualsXmlFile()
, assertXmlStringEqualsXmlFile()
, assertXmlStringEqualsXmlString()
关于单元测试的精彩讨论,请看这里:https://en.wikipedia.org/wiki/Unit_testing。
关于 composer.json 文件指令的更多信息,请看 https://getcomposer.org/doc/04-schema.md。
关于完整的断言列表,请看一下PHPUnit文档页:https://phpunit.de/manual/current/en/phpunit-book.html#appendixes.assertions。
PHPUnit文档还在这里详细介绍了如何使用getMockBuilder():https://phpunit.de/manual/current/en/phpunit-book.html#test-doubles.mock-objects。