如今,讓咱們仔細看看測試結構的樣子。 讓咱們從一個簡單的測試用例開始,它將顯示基本的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反射並動態更改可訪問性。
代碼實現部分待續......