一个函数、一个类编写完成,到底能不能正确工作?怎么测试它?PHP 单元测试是个好办法,它提供了自动化测试的方法,使敏捷开发的自动化测试成为可能。

    在 PHP 下进行单元测试,需要用到 PHP 单元测试的一个框架。这个单元测试框架随 PEAR 即 PHP 扩展库一起分发。所以需要首先安装 Pear 的 PHP 单元测试扩展库。安装是通过网络从有关的站点实时安装的,所以安装的机器必须连接到互联网上。

    一、安装 PEAR

    但从 PHP5.2 版本开始,在 Windows 下安装 PEAR 就有一些问题,直到 PHP5.3,问题依然存在。比如,在 Zend Server Community Edition 5.0.1(包含 PHP5.3.2)下,安装 PEAR,点击运行 ~\Zend\ZendServer\bin\go-pear.bat 批处理文件,会报错:

    “……~\Zend\ZendServer\bin\PEAR\go-pear.phar does not have a signature”

    通过查找资料,修改 go-pear.bat 的第三条语句

    %PHP_BIN% -d output_buffering=0 PEAR\go-pear.phar,为

    %PHP_BIN% -d phar.require_hash=0 PEAR\go-pear.phar

    才会开始安装 PEAR。

    修改后的 go-pear.bat 文件内容:

    @ECHO OFF
    set PHP_BIN=php.exe
    rem %PHP_BIN% -d output_buffering=0 PEAR\go-pear.phar
    %PHP_BIN% -d phar.require_hash=0 PEAR\go-pear.phar
    pause

    二、使用 PEAR 来安装 PHPUnit:

    PEAR 相当于一个管理程序,要使用 PHPUnit 的话,还必须通过 PEAR 来安装它。注意它也是通过网络从有关的站点实时安装的。
    依次运行命令:

    pear channel-discover pear.phpunit.de
    pear channel-discover pear.symfony-project.com
    pear install phpunit/PHPUnit

    但运行第三条命令时,报错,提示 PEAR 版本太低,要求大于等于 1.8.1 版本。于是运行命令 pear upgrade pear 对 PEAR 进行升级(从当前的1.7.2 升级到 1.9.1)。
    然后就可以安装上 PHPUnit 了。

    PEAR 有很多命令行参数,用来管理安装的软件包,比如:
    pear upgrade pear  对 PEAR 自身进行升级
    pear info pear  查看 PEAR 信息
    pear list  列出已经安装的包
    pear list-all  列出所有的包
    pear help  PEAR 帮助信息

    运行 phpunit 命令可以看到 phpunit 的有关信息。

    go-pear 安装的 PEAR 的所有的软件包,都在 ~\Zend\ZendServer\bin\pear 文件夹下,该文件夹被加入到 php.ini 的 include_path 变量中作为库文件的搜索路径之一。

    ;***** Added by go-pear
    include_path=".;C:\Program Files\Zend\ZendServer\bin\pear;C:\Program Files\Zend\ZendServer\share\ZendFramework\library"
    ;*****

    所以在书写测试代码时,语句 require_once 'PHPUnit/Framework.php' 就可以直接寻找到 PHPUnit 的库文件。

    三、使用 PHPUnit 进行单元测试

    3.1 下面的例子用来测试 sizeof 函数工作的正确性:

        require_once 'PHPUnit/Framework.php';
    class ArrayTest extends PHPUnit_Framework_TestCase
    {
      public function testNewArrayIsEmpty()
      {
        /*Create the Array fixture*/
        $fixture = array();
        /* Assert that the size of the Array * fixture is 0*/
        $this->assertEquals(0, sizeof($fixture));
      }

      public function testArrayContainsAnElement()
      {
        /* Create the Array fixture*/
        $fixture = array();
        /*Add an element to the Array * fixture*/
        $fixture[] = 'Element';
        /*Assert that the size of the * Array fixture is 1*/
        $this->assertEquals(1, sizeof($fixture));
      }
    }
    ?>

    要点:
    require_once 'PHPUnit/Framework.php' 语句是必须的。
    编写的测试用例(usecase)是一个 PHP 脚本,需要在测试用例脚本中包含需要测试的代码。
    测试用例的主体必须写在类中,类名建议和文件名保持一致,还必须是 PHPUnit_Framework_TestCase 的派生类。
    每一个测试用例都是一个 public 类型的成员函数,必须以 test 开头。
    程序的输出使用 assert* 系列函数(assertEquals、assertNotEquals、assertTrue、assertFalse、assertSame、setExpectedException)来进行验证(参见 http://www.phpunit.de 网站上的 PHPUnit 手册)。

    运行:
    该测试用例需要在命令窗口下键入 phpunit ArrayTest.php 来运行,而不是用浏览器打开它。结果如下所示:

    C:\Program Files\Zend\Apache2\htdocs\unit_test>phpunit ArrayTest.php
    PHPUnit 3.4.14 by Sebastian Bergmann.
    ..
    Time: 1 second, Memory: 3.75Mb
    OK (2 tests, 2 assertions)

    其中的两个点“..”表示,两个测试用例全都通过。

    添加一个成员函数:

    public function testArrayContains2Element()
    {
      $fixture = array();
      $fixture[] = 'Element';
      $this->assertTrue(sizeof($fixture)==2);
    }

    然后再次测试,报告:

    PHPUnit 3.4.14 by Sebastian Bergmann.
    ..F
    Time: 0 seconds, Memory: 3.75Mb
    There was 1 failure:

    1) ArrayTest::testArrayContains2Element
    Failed asserting that is true.

    C:\Program Files\Zend\Apache2\htdocs\unit_test\ArrayTest.php:27

    FAILURES!
    Tests: 3, Assertions: 3, Failures: 1.

    其中的“..F”表示,两个测试用例通过,一个失败。并提示了出错的位置。

    PHPUnit 命令行工具输出的指示字符:

    .  当测试成功时输出
    F  当运行测试方法过程中一个断言失败时输出
    E  当运行测试方法过程中产生一个错误时输出
    S  当测试被跳过时输出
    I  当测试被标记为不完整或未实现时输出

    PHPUnit 的失败和错误是不同的。失败是指违反 PHPUnit 语法,例如 assertEquals() 调用失败。错误是指异常或 PHP 错误。有时这种区分非常有用,因为错误通常比失败容易修复。如果你得到一大串问题,最好先解决所有错误,然后再看还有没有失败。

    3.2 使用数据提供者

    测试方法可接受参数。这些参数将由一个数据提供者方法提供(provider())。要用到的数据提供着方法是用@dataProvider注解指定的。

    数据提供者方法必须是public和static的,而且返回值必须是数组的数组或者实现了Iterator接口并且每次迭代产生一个数组的对象。对于每个数组(即集合的部分),都将以其内容为参数调用测试方法。

          class DataTest extends PHPUnit_Framework_TestCase
      {
       public static function provider()
       {
        return array(
          array(0, 0, 0),
          array(0, 1, 1),
          array(1, 0, 1),
          array(1, 1, 3)
        );
        }

        /**
         * @dataProvider provider
         */
        public function testAdd($a, $b, $c)
        {
         $this->assertEquals($c, $a + $b);
        }
      }
    ?>


    附录-1:安装 PEAR 时的输出记录:

    ==================================================================
    C:\Program Files\Zend\ZendServer\bin>go-pear.bat

    Are you installing a system-wide PEAR or a local copy?
    (system|local) [system] :

    Below is a suggested file layout for your new PEAR installation.
    To change individual locations, type the number in front of the directory.
    Type 'all' to change all of them or simply press Enter to
    accept these locations.

     1. Installation base ($prefix)                   : C:\Program Files\Zend\ZendServer\bin
     2. Temporary directory for processing            : C:\Program Files\Zend\ZendServer\bin\tmp
     3. Temporary directory for downloads             : C:\Program Files\Zend\ZendServer\bin\tmp
     4. Binaries directory                            : C:\Program Files\Zend\ZendServer\bin
     5. PHP code directory ($php_dir)                 : C:\Program Files\Zend\ZendServer\bin\pear
     6. Documentation directory                       : C:\Program Files\Zend\ZendServer\bin\docs
     7. Data directory                                : C:\Program Files\Zend\ZendServer\bin\data
     8. User-modifiable configuration files directory : C:\Program Files\Zend\ZendServer\bin\cfg
     9. Public Web Files directory                    : C:\Program Files\Zend\ZendServer\bin\www
    10. Tests directory                               : C:\Program Files\Zend\ZendServer\bin\tests
    11. Name of configuration file                    : C:\WINDOWS\pear.ini
    12. Path to CLI php.exe                           : C:\Program Files\Zend\ZendServer\bin\.

    1-12, 'all' or Enter to continue:
    Beginning install...
    Configuration written to C:\WINDOWS\pear.ini...
    Initialized registry...
    Preparing to install...
    installing phar://C:/Program Files/Zend/ZendServer/bin/PEAR/go-pear.phar/PEAR/go
    -pear-tarballs/Archive_Tar-1.3.2.tar...
    installing phar://C:/Program Files/Zend/ZendServer/bin/PEAR/go-pear.phar/PEAR/go
    -pear-tarballs/Console_Getopt-1.2.3.tar...
    installing phar://C:/Program Files/Zend/ZendServer/bin/PEAR/go-pear.phar/PEAR/go
    -pear-tarballs/PEAR-1.7.2.tar...
    installing phar://C:/Program Files/Zend/ZendServer/bin/PEAR/go-pear.phar/PEAR/go
    -pear-tarballs/Structures_Graph-1.0.2.tar...
    pear/PEAR can optionally use package "pear/XML_RPC" (version >= 1.4.0)
    install ok: channel://pear.php.net/Archive_Tar-1.3.2
    install ok: channel://pear.php.net/Console_Getopt-1.2.3
    install ok: channel://pear.php.net/Structures_Graph-1.0.2
    install ok: channel://pear.php.net/PEAR-1.7.2
    PEAR: Optional feature webinstaller available (PEAR's web-based installer)
    PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
    PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)

    PEAR: To install optional features use "pear install pear/PEAR#featurename"

    ******************************************************************************
    WARNING!  The include_path defined in the currently used php.ini does not
    contain the PEAR PHP directory you just specified:
   
    If the specified directory is also not in the include_path used by
    your scripts, you will have problems getting any PEAR packages working.

    Would you like to alter php.ini ?
    [Y/n] : y

    php.ini include_path updated.

    Current include path           : .;C:\Program Files\Zend\ZendServer\share\ZendFr
    amework\library
    Configured directory           : C:\Program Files\Zend\ZendServer\bin\pear
    Currently used php.ini (guess) : C:\Program Files\Zend\ZendServer\etc\php.ini
    Press Enter to continue:

    ** WARNING! Old version found at C:\Program Files\Zend\ZendServer\bin, please re
    move it or be sure to use the new c:\program files\zend\zendserver\bin\pear.bat
    command

    The 'pear' command is now at your service at c:\program files\zend\zendserver\
    bin\pear.bat

    ** The 'pear' command is not currently in your PATH, so you need to
    ** use 'c:\program files\zend\zendserver\bin\pear.bat' until you have added
    ** 'C:\Program Files\Zend\ZendServer\bin' to your PATH environment variable.

    Run it without parameters to see the available actions, try 'pear list'
    to see what packages are installed, or 'pear help' for help.

    For more information about PEAR, see:

    http://pear.php.net/faq.php
    http://pear.php.net/manual/

    Thanks for using go-pear!

    * WINDOWS ENVIRONMENT VARIABLES *
    For convenience, a REG file is available under C:\Program Files\Zend\ZendServer\
    bin\PEAR_ENV.reg .
    This file creates ENV variables for the current user.

    Double-click this file to add it to the current user registry.

    请按任意键继续. . .

    ==================================================================

    安装完毕,按照提示双击 C:\Program Files\Zend\ZendServer\bin\PEAR_ENV.reg 文件,向注册表写入 PHP_PEAR_SYSCONF_DIR、PHP_PEAR_INSTALL_DIR 等键值。

    PEAR_ENV.reg 文件内容为:

    REGEDIT4
    [HKEY_CURRENT_USER\Environment]
    "PHP_PEAR_SYSCONF_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin"
    "PHP_PEAR_INSTALL_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin\\pear"
    "PHP_PEAR_DOC_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin\\docs"
    "PHP_PEAR_BIN_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin"
    "PHP_PEAR_DATA_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin\\data"
    "PHP_PEAR_PHP_BIN"="C:\\Program Files\\Zend\\ZendServer\\bin\\.\\php.exe"
    "PHP_PEAR_TEST_DIR"="C:\\Program Files\\Zend\\ZendServer\\bin\\tests"

    附录-2 添加频道 channel-discover pear.phpunit.de
    ==================================================================
    C:\Program Files\Zend\ZendServer\bin>pear channel-discover pear.phpunit.de
    Adding Channel "pear.phpunit.de" succeeded
    Discovery of channel "pear.phpunit.de" succeeded

    附录-3 添加频道 pear.symfony-project.com
    ======================================================================
    C:\Program Files\Zend\ZendServer\bin>pear channel-discover pear.symfony-project.com
    Adding Channel "pear.symfony-project.com" succeeded
    Discovery of channel "pear.symfony-project.com" succeeded

    附录-4 安装 PHPUnit
    报错:当前的 PEAR 版本是 1.7.2,太低,需要 version >= 1.8.1
    ======================================================================
    C:\Program Files\Zend\ZendServer\bin>pear install phpunit/PHPUnit
    Did not download optional dependencies: pear/Image_GraphViz, pear/Log, symfony/YAML,
    use --alldeps to download automatically
    phpunit/PHPUnit requires PEAR Installer (version >= 1.8.1), installed version is 1.7.2
    phpunit/PHPUnit can optionally use package "pear/Image_GraphViz" (version >= 1.2.1)
    phpunit/PHPUnit can optionally use package "pear/Log"
    phpunit/PHPUnit can optionally use package "symfony/YAML" (version >= 1.0.2)
    phpunit/PHPUnit can optionally use PHP extension "xdebug" (version >= 2.0.5)
    No valid packages found

    附录-5 升级 PEAR(到 PEAR-1.9.1)
    ======================================================================
    C:\Program Files\Zend\ZendServer\bin>pear upgrade pear
    downloading PEAR-1.9.1.tgz ...
    Starting to download PEAR-1.9.1.tgz (293,587 bytes)
    ...................................................done: 293,587 bytes

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    downloading Archive_Tar-1.3.7.tgz ...
    Starting to download Archive_Tar-1.3.7.tgz (17,610 bytes)
    ...done: 17,610 bytes

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    downloading Structures_Graph-1.0.3.tgz ...
    Starting to download Structures_Graph-1.0.3.tgz (30,191 bytes)
    ...done: 30,191 bytes

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    downloading XML_Util-1.2.1.tgz ...
    Starting to download XML_Util-1.2.1.tgz (17,729 bytes)
    ...done: 17,729 bytes

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    upgrade ok: channel://pear.php.net/Archive_Tar-1.3.7

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    upgrade ok: channel://pear.php.net/Structures_Graph-1.0.3

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    upgrade ok: channel://pear.php.net/XML_Util-1.2.1

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489

    Warning: strtotime(): It is not safe to rely on the system's timezone settings.
    You are *required* to use the date.timezone setting or the date_default_timezone
    _set() function. In case you used any of those methods and you are still getting
     this warning, you most likely misspelled the timezone identifier. We selected '
    UTC' for '8.0/no DST' instead in PEAR\Validate.php on line 489
    upgrade ok: channel://pear.php.net/PEAR-1.9.1
    PEAR: Optional feature webinstaller available (PEAR's web-based installer)
    PEAR: Optional feature gtkinstaller available (PEAR's PHP-GTK-based installer)
    PEAR: Optional feature gtk2installer available (PEAR's PHP-GTK2-based installer)

    PEAR: To install optional features use "pear install pear/PEAR#featurename"

    附录-6 再次安装 PHPUnit
    成功安装 PHPUnit-3.4.14
    ======================================================================
    C:\Program Files\Zend\ZendServer\bin>pear install phpunit/PHPUnit
    Did not download optional dependencies: pear/Image_GraphViz, pear/Log, symfony
    /YAML, use --alldeps to download automatically
    phpunit/PHPUnit can optionally use package "pear/Image_GraphViz" (version >= 1.2.1)
    phpunit/PHPUnit can optionally use package "pear/Log"
    phpunit/PHPUnit can optionally use package "symfony/YAML" (version >= 1.0.2)
    phpunit/PHPUnit can optionally use PHP extension "xdebug" (version >= 2.0.5)
    downloading PHPUnit-3.4.14.tgz ...
    Starting to download PHPUnit-3.4.14.tgz (254,983 bytes)
    .....................................................done: 254,983 bytes
    install ok: channel://pear.phpunit.de/PHPUnit-3.4.14

    ======================================================================
    相关链接:
    PHPUnit官方:http://www.phpunit.de/
    PHPUnit文档:http://www.phpunit.de/pocket_guide/3.2/en/index.html

    作者:张庆(网眼) 西安 PHP 教育培训中心 2010-7-2
    来自“网眼视界”:http://blog.why100000.com
    作者微博:http://t.qq.com/zhangking
    “十万个为什么”电脑学习网:http://www.why100000.com