如今,让咱们仔细看看测试结构的样子。 让咱们从一个简单的测试用例开始,它将显示基本的PHPUnit测试结构。 如下代码片断是测试用于排序数组的两个PHP函数的一个很是基本的示例:asort()用于对数组进行排序并维护索引,而ksort()用于按键对数组进行排序。 首先,咱们有一系列蔬菜,其中名称是关键,价格是价值:php
<?php class SecondTest extends PHPUnit_Framework_TestCase { public function testAsort () { $vegetablesArray = array ( 'carrot' => 1, 'broccoli' => 2.99, 'garlic' => 3.98, 'swede' => 1.75 ); $sortedArray = array ( 'carrot' => 1, 'swede' => 1.75, 'broccoli' => 2.99, 'garlic' => 3.98 ); asort( $vegetablesArray, SORT_NUMERIC ); $this->assertSame( $sortedArray, $vegetablesArray ); } public function testKsort () { $fruitsArray = array ( 'oranges' => 1.75, 'apples' => 2.05, 'bananas' => 0.68, 'pear' => 2.75 ); $sortedArray = array ( 'apples' => 2.05, 'bananas' => 0.68, 'oranges' => 1.75, 'pear' => 2.75 ); ksort( $fruitsArray, SORT_STRING ); $this->assertSame( $sortedArray, $fruitsArray ); } }
如您所见,每一个测试用例都是一个类。 类名应以Test结尾。 这是由于当您测试类时,测试类应该镜像测试类,而且测试类和测试套件之间的区别是测试添加到名称:面试
. |-- src '-- library '-- Util '-- File.php |-- tests '-- library '-- Util '-- FileTest.php
每一个测试类也称为测试套件,它扩展了PHPUnit_Framework_TestCase类。 固然,您能够编写本身的修改或扩展的PHPUnit_ Framework_TestCase并调用它,例如,MyTestCase,但这个父类应该扩展原始的PHPUnit类。 这能够在如下类定义中看到:数据库
abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test,PHPUnit_Framework_SelfDescribing
如下内容告诉您前面的类定义的做用:编程
PHPUnit不使用已经引入PHP 5.3的名称空间。 主要是为了向后兼容,PHPUnit在类名中使用下划线来匹配文件系统中的类位置。数组
1.定义测试方法bash
每一个测试方法名称都应该以test开头。 那么这个名字取决于你。 您能够镜像他们的测试类方法,但您可能须要更多测试。 每一个测试方法都应该有一个解释性名称,这意味着您应该可以从名称中说出您正在测试的内容,例如testChangePassword()或testChangePasswordForLockedAcoounts()app
2.测试函数函数
这是一个理论,如今咱们来看看一个特定的现实生活中的例子。 让咱们使用开发人员在求职面试中能够获得的如下问题,并经过此示例了解PHPUnit如何帮助咱们解决问题或让咱们确信咱们找到了正确的解决方案。单元测试
任务是在PHP中编写一个函数,它返回有序数组中最大的连续整数和。测试
例如,若是输入为[0,1,2,3,6,7,8,9,11,12,14],则最大总和为6 + 7 + 8 + 9 = 30。
如您所见,它看起来并不特别困难。 您能够采起几种不一样的方法。 写一些很是聪明的东西,使用蛮力和遍历数组,但任何有效的方法都是正确的答案。
如今,让咱们来看看PHPUnit如何帮助解决这个问题。 首先,你必须停下来思考你在作什么 - 不只仅是开始编写代码,而且在一段时间后迷失方向,不知道你想要实现什么。
那么你如何解决这个问题呢?
如下步骤是最好的方法:
1.使用PHP函数usort()使用用户定义的比较函数按值对数组进行排序。
2.而后返回已排序数组的最后一个元素,该元素将成为最大的组。
这是制定战略的重要一点。 如今,让咱们将其转换为函数,而无需编写实现。 请考虑如下代码段:
<?php /** * 返回连续整数的最大总和 * @param array $inputArray * @return array */ function sumFinder(array $inputArray) : array {} /** * 自定义数组比较方法 * @param array $a * @param array $b * @return int */ function compareArrays(array $a, array $b) : int {}
如今你有两个功能。 第一个函数使用PHP函数usort()将数组做为输入,第二个函数compareArrays()按组对数组进行排序。 而后第一个函数返回一个包含组名和sum的数组。
如今你知道函数的样子了。 让咱们为它们编写测试,描述它们的行为方式,以下面的代码片断所示:
<?php require_once 'SumFinder.php'; class SumFinderTest extends PHPUnit_Framework_TestCase { public function testSumFinder () { $input = array ( 0, 1, 2, 3, 6, 7, 8, 9, 11, 12, 14 ); $result = array ( 'group' => '6, 7, 8, 9', 'sum' => 30 ); $this->assertEquals( $result, sumFinder( $input ) ); } public function testCompareArrays () { $array1 = array ( 0, 1, 2, 3 ); $array2 = array ( 6, 7, 8, 9 ); // $array2 > $array1 $this->assertEquals( -1, compareArrays( $array1, $array2 ) ); // $array1 < $array2 $this->assertEquals( 1, compareArrays( $array2, $array1 ) ); // $array2 = $array2 $this->assertEquals( 0, compareArrays( $array2, $array2 ) ); } }
如您所见,每一个函数都有两个简单的测试。 第一个测试testSumFinder()验证主sumFinder()函数,第二个测试testCompareArrays()验证在使用usort()时数组是否正确排序。 如今您能够运行它们并查看如下结果:
FAILURES! Tests: 2, Assertions: 2, Failures: 2.
牛逼! 这就是咱们想要的:它失败了,由于咱们必须编写一个实现。让咱们从compareArrays()函数开始,以下面的代码片断所示:
<?php /** * 自定义数组比较方法 * @param array $a * @param array $b * @return int */ function compareArrays ( array $a, array $b ) { $sumA = array_sum( $a ); $sumB = array_sum( $b ); if ($sumA == $sumB) { return 0; } else if ($sumA > $sumB) { return 1; } else { return -1; } }
而后你能够运行testCompareArrays(),很好,它的工做方式以下面的输出:
OK (1 test, 3 assertions)
如今咱们能够转到sumFinder()实现,以下面的代码片断所示:
<?php /** * 返回连续整数的最大总和 * @param array $inputArray * @return array */ function sumFinder ( array $inputArray ) : array { $arrayGroups = array (); foreach ($inputArray as $element) { //initial settings if (!isset( $previousElement )) { $previousElement = $element; $arrayGroupNumber = 0; } if (( $previousElement + 1 ) != $element) { $arrayGroupNumber += 1; } $arrayGroups[ $arrayGroupNumber ][] = $element; $previousElement = $element; } usort( $arrayGroups, 'compareArrays' ); $highestGroup = array_pop( $arrayGroups ); return ( array ( 'group' => implode( ', ', $highestGroup ), 'sum' => array_sum( $highestGroup ) ) ); }
运行上面的代码段时,您将得到如下结果:
OK (1 test, 1 assertion)
这就是解决方案。 在这里,您应该看到单元测试的最大优点之一; 它会强迫您在开始编写代码或在实现以前阐明代码,考虑您正在作什么并有一个清晰的想法。
3.测试方法
测试方法与测试函数很是类似,可是你能够作更多。过程编程没有任何问题,可是有很好的理由使用面向对象编程(OOP)。 PHP严格地说不是Java或C#等OOP语言,但这并不意味着你不能使用OOP。 固然能够,PHP有一个很是好的对象模型。 它是一个可靠的实现,具备您指望的全部主要功能。 OOP真正有用的地方在于组织具备全部OOP优点的代码,而且在编写测试时也有帮助。
PHPUnit支持测试类和对象。 在测试类时,PHPUnit和单元测试的真正优点在于您不只能够测试每一个方法,并且在必要时,您可使用您的实现替换部分类以进行测试,例如,避免将数据存储在数据库中。 这称为测试双打,这些技术将在本书的后面部分进行描述。
在测试类方法时,建议仅测试公共方法。 听说尽量多的代码应该进行测试后,这听起来有点奇怪。 为何不为私有或受保护的方法编写测试? 缘由是你应该只测试公共方法; 其余一切都只是你不该该担忧的内部实现,即便它发生了变化,公共方法仍然应该以相同的方式运行。 若是私有/受保护的方法过于复杂,则可能代表它们应该拥有本身的类,或者您可使用PHP反射并动态更改可访问性。
代码实现部分待续......