第二節 PHPUnit測試的剖析

       如今,讓咱們仔細看看測試結構的樣子。 讓咱們從一個簡單的測試用例開始,它將顯示基本的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

       如下內容告訴您前面的類定義的做用:編程

  • abstract:這是由於你老是擴展PHPUnit_Framework_TestCase類而不直接執行這個類
  • 擴展PHPUnit_Framework_Assert:這是提供一組斷言方法的類,例如assertTrue()和assertEquals()
  • 實現PHPUnit_Framework_Test:這是一個簡單的接口,只包含一個方法run()來執行測試
  • 實現PHPUnit_Framework_SelfDescribing:這是另外一個簡單的接口,只包含一個方法toString()

       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反射並動態更改可訪問性。

   代碼實現部分待續......

相關文章
相關標籤/搜索