浅析PHP官方自动化测试方法
 
2009-04-09 作者:林容容 来源:IBM
 

本文将从一个测试人员的角度对 PHP 官方的自动化测试方法,自动化测试框架结构,以及具体的实现进行分析和研究。通过实际的例子,向读者展示 PHP 是如何实现有效的自动化测试以保证其代码高质量的。读者通过阅读本文,可以学习 PHP 官方自动化测试方法和实现,并将这种自动化测试方法应用到自己的开发过程中,提高代码功能质量。

PHP 官方自动化测试方法简述

以下介绍以 PHP 最新官方版本 5.2.8 的源码在 LINUX 系统平台上的分析为例展开。首先来看一下 PHP 的自动化测试脚本 PHPT 脚本。

自动化测试脚本 PHPT 示例

PHP 的测试脚本是以“ .phpt ”为后缀,包含 TEST,FILE,EXPECT 等多个段落的文件,简称 PHPT 。在各个段落中,TEST,FILE,EXPECT 是基本的段落,每个测试脚本都必须至少包括这三个段落。其中,TEST 段可以用来填写测试用例的名字; FILE 段是一个 PHP 脚本实现的测试用例; EXPECT 段则是测试用例的期待值。测试用例的运行中,PHP 将用被测试的 PHP 可执行对象去运行 FILE 段中的测试用例,用实际的结果去比对测试用例中 EXPECT 段所列的期待值;如果实际结果和期待值一致,则测试通过;如果不一致,则测试失败。

表 1 列出的是常用的段落名和其相应的填充内容说明。

表 1. PHP 测试脚本中的段落说明

段落名 填充内容 备注
TEST 测试用例名称 必填段落
ARGS FILE 段的输入参数 选填段落
SKIPIF 跳过这个测试的条件 选填段落
POST 传入测试脚本的 POST 变量 选填段落。如果使用 POST 段,建议配合使用 SKIPIF 段,如:
--SKIPIF--
<?php if (php_sapi_name()=='cli') echo 'skip'; ?>
GET 传入测试脚本的 GET 变量 选填段落。如果使用 POST 段,建议配合使用 SKIPIF 段,如:
--SKIPIF--
<?php if (php_sapi_name()=='cli') echo 'skip'; ?>
INI 应用于测试脚本的 ini 设置 选填段落。例如 foo=bar 。其值可通过函数 ini_get(string name_entry) 获得。
FILE 测试脚本语句 必填段落。应用 PHP 语言书写的脚本语句。其执行的结果将与 EXPECT* 段的期待值做对比。
EXPECT 测试脚本的期待值 必填段落
EXPECTF 测试脚本的期待值,可用函数 sscanf() 中的格式表达期待值 EXPECT 段的变体
EXPECTREGEX 测试脚本的期待值,可用正则式表达期待值 EXPECT 段的变体

以官方包里自带的测试脚本 “ 001.phpt ” 为例(见清单 1),从 TEST 段的内容看来这是一个对 PHP 版本进行验证的测试用例。 SKIPIF 段的内容写在了 “ skipip.inc ” 文件里。在 FILE 段里,测试用例将环境变量 TEST_PHP_EXECUTABLE 里设置的那个 PHP 的版本打印出来,这个结果将和 EXPECTF 中的字串进行格式匹对。

清单 1. PHPT 测试脚本 “ 001.phpt ” 示例

 --TEST--
 version string
 --SKIPIF--
 <?php include "skipif.inc"; ?>
 --FILE--
 <?php

 $php = getenv('TEST_PHP_EXECUTABLE');

 var_dump(`$php -n -v`);

 echo "Done\n";
 ?>
 --EXPECTF--  
 string(%d) "PHP %s (cli) (built: %s)%s
 Copyright (c) 1997-20%d The PHP Group
 Zend Engine v%s, Copyright (c) 1998-20%d Zend Technologies
 "
 Done

如何运行 PHP 自动化测试脚本

在运行测试脚步前,首先要将被测试的 PHP 源码编译为可执行对象。

然后要导入若干环境变量。表 2 中介绍了主要的几个环境变量如何设置。

表 2. PHP 自动化测试中的环境变量设置

环境变量名 环境变量值 例子
TEST_PHP_EXECUTABLE 设定被测试对象 PHP,或者 “ auto ” 。当设置 “ auto ” 时,如果是 CGI 模式,即为 “ ./sapi/cgi/php-cgi ” ;如果是 CLI 模式,即为 “ ./sapi/cli/php ” 。 TEST_PHP_EXECUTABLE=
$HOME/php-5.2.8/sapi/cli/php
TEST_PHP_DETAILED 设定是否需要详细的日志输出。设置值为 1 或者 0 。 TEST_PHP_DETAILED=1
TEST_PHP_USER 设定是否需要特制的用户目录。 TEST_PHP_DETAILED= “ /usr/test1 ”
 
TEST_PHP_LOG_FORMAT
 
设定日志的格式。设置值为 “ LEOD ” 子串的子集。其中 L 代表测试后需要生成 “ .log ” 文件,E 代表 “ .exp ” ,O 代表 “ .out ” ,D 代表 “ .diff ” 。 TEST_PHP_LOG_FORMAT="LD"

在本例中,在 Bash 环境中设置环境变量如下:

清单 2. 设置环境变量示例

export HOME=/home/user_dir/
 export TEST_PHP_EXECUTABLE=$HOME/php-5.2.8/sapi/cli/php
 export TEST_PHP_DETAILED=1
 export TEST_PHP_LOG_FORMAT="LEOD"

经过这样设置后,被测试的 PHP 可执行对象就是放在目录 “ $HOME/php-5.2.8/sapi/cli/ ” 下编译好的那个 “ php ” 可执行文件。

执行测试前,还需将测试脚本 PHPT 编辑好,存为 “ .phpt ” 文件。这里以官方包里自带的测试脚本 “ 001.phpt ” 为例,运行如下:

清单 3. PHPT 测试总结报告实例

bash-2.03$ cd $HOME/php-5.2.8/
 bash-2.03$ $HOME/php-5.2.8/sapi/cli/php run-tests.php  \
 $HOME/php-5.2.8/sapi/cli/tests/001.phpt

如果该测试用例的实际输出与期待值一致,则在屏幕上输出测试结果如下:

清单 4. PHPT 测试总结报告实例

=====================================================================
 CWD         : /home/user_dir/php-5.2.8/sapi/cli/php
 PHP         : /home/user_dir/php-5.2.8/sapi/cli/php
 PHP_SAPI    : cli
 PHP_VERSION : 5.2.8
 ZEND_VERSION: 2.1.0
 PHP_OS      : Linux rhas05 2.6.9-55.ELhugemem #1
                       SMP Fri Apr 20 17:20:11 EDT 2007 i686 i686 i386 GNU/Linux
 INI actual  :
 More .INIs  :
 Extra dirs  :
 =====================================================================
 Running selected tests.
 PASS Test version string [001.phpt]
 =====================================================================
 Number of tests :    1                 1
 Tests skipped   :    0 (  0.0%) --------
 Tests warned    :    0 (  0.0%) (  0.0%)
 Tests failed    :    0 (  0.0%) (  0.0%)
 Tests passed    :    1 (100.0%) (100.0%)
 ---------------------------------------------------------------------
 Time taken      :    0 seconds
 =====================================================================

如果该测试失败了,则除了屏幕输出失败结果外,当前运行目录下还会生成若干文件,以供用户分析测试失败的原因。生成的日志文件的种类是由环境变量 TEST_PHP_LOG_FORMAT 设定的,详见表 2 。如果设定的是 “ LEOD ” 则生成日志文件包括表 3 中列出的五种文件。

表 3. PHP 自动化测试的输出脚本文件

日志文件名 日志文件内容 对应的TEST_PHP_LOG_FORMAT里的设置
001.out 运行测试语句后得到的实际输出结果。 O
001.exp 脚本中的期待结果,即测试脚本中 EXPECT* 段的内容。 E
001.log 实际运行的输出结果和脚本中的期待结果,即 “ .exp ” 和 “ .out ” 的合集。 L
001.diff 实际运行的输出结果和脚本中的期待结果通过 diff 命令得到比对后的结果。 D
001.php 实际执行的 PHP 测试语句,在测试中解析测试脚本的 FILE 段得到。 总会生成

上面的例子是一次只运行一个脚本。 PHP 也支持多测试脚本一起运行。表 4 列出了三种 PHP 支持的测试方式。

表 4. PHP 自动化测试方式

测试方式 参数 举例
只执行单个测试用例 测试脚本名 001.phpt 002.phpt
执行某个目录下的测试用例 测试用例的目录名 test_dir/
执行某个文件中列出的测试用例 -r 加列举测试用例的文件名 -r record_file

PHP 自动化测试框架的原理和实现

在清单 3中的运行示例中,实际的执行语句是 “ $HOME/php-5.2.8/sapi/cli/php run-tests.php $HOME/php-5.2.8/sapi/cli/tests/001.phpt ” 。其中,“ 001.phpt ” 是测试脚本;“ run-tests.php ” 为 PHP 测试的驱动脚本,是官方脚本;而 “ $HOME/php-5.2.8/sapi/cli/php ” 是运行驱动脚本的 PHP 可执行对象。这里需要指出的一点是,环境变量 TEST_PHP_EXECUTABLE 中设置的 PHP 可执行对象和这里运行测试脚本中的 “ $HOME/php-5.2.8/sapi/cli/php ” 虽然指向的是同一个可执行对象,但其意义是不一样的。环境变量 TEST_PHP_EXECUTABLE 中设置的 PHP 是被测试对象,而运行测试中使用的 “ $HOME/php-5.2.8/sapi/cli/php ” 只是为了用来驱动测试脚本运行。

在图 1 中可以具体的看出 PHP 测试过程中两个 PHP 可执行对象及两个 PHP 脚本的关系。在测试的过程中,首先是由 “ $HOME/php-5.2.8/sapi/cli/php ” 去运行脚本 “ run-tests.php ” (第 1 步)。 在 “ run-tests.php ” 脚本中,将解析环境变量的值 “ TEST_PHP_EXECUTABLE =$HOME/php-5.2.8/sapi/cli/php ” (第 2 步)。这里 PHP 可执行对象是被测试的 PHP 。“ run-tests.php ” 脚本中还将把测试脚本 “ 001.phpt ” 中的 FILE 段解析为 PHP 脚本 “ 001.php ” (第 3 步), 用 “ TEST_PHP_EXECUTABLE ” 设置的那个 PHP 可执行对象去执行 “ 001.php ” (第 4 步)并得到实际输出结果,在比对实际输出结果和 EXPECT 段的期待结果后,输出测试结果(第 5 步)。以上运行过程可以从对 “ run-tests.php ” 脚本的分析中得出。

图 1. PHP 测试运行关系图

通过以上实例分析可以看出,PHP 官方测试的自动化主要依赖于 “ run-tests.php ” 脚本和测试用例脚本 PHPT 。而 “ run-tests.php ” 脚本正是 PHP 测试自动化框架的搭建者。从代码清单 5 中,可以略微看出 PHP 自动化测试框架的主要工作。

清单 5. PHP 自动化测试代码片段

$test_cnt = count($test_files);

  if ($test_cnt) {
   putenv('NO_INTERACTION=1');
   verify_config();
   write_information($html_output);
   usort($test_files, "test_sort");
   $start_time = time();

   if (!$html_output) {
    echo "Running selected tests.\n";
   } else {
    show_start($start_time);
   }

   $test_idx = 0;
   run_all_tests($test_files, $environment);
   $end_time = time();

   if ($html_output) {
    show_end($end_time);
   }

   if ($failed_tests_file) {
    fclose($failed_tests_file);
   }

   if (count($test_files) || count($test_results)) {
    compute_summary();
    if ($html_output) {
    fwrite($html_file, "<hr/>\n" . get_summary(false, true));
    }
    echo "=================================================";
    echo get_summary(false, false);
   }

   if ($html_output) {
    fclose($html_file);
   }

   if ($output_file != '' && $just_save_results) {
    save_or_mail_results();
   }

        if (getenv('REPORT_EXIT_STATUS') == 1 and preg_match('/FAILED(?: |$)/',   \
                    implode(' ', $test_results))) {
    exit(1);
   }

在测试前,这个自动化测试的框架按照用户指定的有效测试文件数目来决定具体将有多少个测试用例,并写入 test_cnt 变量中以备计数使用。函数 verify_config() 分别用来验证和准备各种环境变量的设置及 ini 设置。函数 write_information() 将输出本次测试环境的各种信息,包括被测试 PHP 的版本号,使用的扩展( Extention ),ZEND 版本,INI 设置,测试所在的系统版本信息,以及测试时间记录。

测试用例的运行发生在函数 run_all_tests() 中调用的函数 run_test() 中。 run_test() 将具体解析测试脚本中各个段落的含义,清除所以上次测试的记录与设置将干净的测试环境准备完毕,并把各种中间文件和日志文件准备好,然后用环境变量 TEST_PHP_EXECUTABLE 指定的 PHP 可执行对象运行实际的测试语句。最后将运行后的结果和测试脚本中期待值进行比对,如果比对失败,则将结果信息一一记录到用户设置的日志文件中。

在所有的测试都运行结束后,变量 end_time 将记录测试的结束时间,并用函数 compute_summary() 计算成功的、失败的、跳过的等各种情况的测试数目,并将结果输出。

由此可以总结出,这个自动化测试的框架主要包括如下几个部分:

1. 测试前的准备:包括环境准备以及测试脚本的解析等,如对上次测试遗留下的环境的清理,本次测试所必须的环境变量的读取与设置,对测试参数的解析,测试脚本名的解析,各种输出文件的准备等等。

2. 测试中的脚本运行:解析测试脚本中的各个段落,组织出测试语句,执行测试语句,得到实际运行结果。

3. 测试后的结果比对及输出:测试后完成实际输出结果和期待值的比对,包括各种格式化的比对和正则表达式方式的比对。按要求将结果存写入指定的文件中,并输出测试结果的总结报告。

PHP 自动化测试方法的应用

了解了 PHP 官方自动化测试的运作方法后,用户可以创建自己的 PHPT 测试脚本以帮助 PHP 进行函数黑盒测试。然而,应用 PHP 官方自动化测试的思想和框架搭建方法,用户还可以构建自己的自动化测试工具,以应用于其他程序开发测试中。在定制自动化测试框架时,需要注意的一些问题,而这些问题在 PHP 官方自动化测试框架中就得到了很好的解决。比如:

1. 测试脚本的结构。 PHP 官方自动化测试框架只需使用一个简单的 PHPT 脚本,就可完成测试用例所有必须信息的生成。 PHPT 脚本中的段落使测试名,测试条件,测试步骤,期待值等信息一目了然,非常有利于阅读和分析。当测试人员在测试过程中需要分析某个测试的失败原因时,只需要打开对应的 PHPT 脚本就能够清楚地理解测试用例的用意以及具体测试语句。从而大大减少测试人员的分析时间和难度。而且,这种段落式的组织是具有可扩展性的。

2. 测试环境的保证。在测试中,环境的“干净”与否直接影响到测试结果的准确性。好的自动化框架要力争给测试一个“干净”的环境。这里的环境包括:系统设置的环境变量,用户设置的环境变量,测试运行中可能用到的临时文件等。如果前一个测试改变了全局的环境,那么在这个环境里运行后一个测试所得到的结果就是不准确的。不准确的测试结果可能带来极高的误报率。

3. 测试结果对比中的艺术。在将测试的实际输出结果和期待结果进行比对中,PHP 官方自动化测试方法巧妙地应用了正则式比对和格式比对方法。这两种方法的引入使得测试的误报率大大降低。因为如果没有正则式比对和格式比对方法,则测试人员只能将当前本次测试期待结果记录在期待值中,而这种期待值将可能成为测试脚本的硬码( hard code ),在回归测试或期待结果是随机值等情况下造成测试的误失败。

4. 保留充分的日志信息。测试结束后,对于那些失败的测试,测试人员要分析失败的原因。此时,测试日志将发挥很重要的作用。充分的日志信息保留了测试失败的现场,将帮助测试人员尽快找到失败的原因并报告给开发人员。 PHP 官方自动化测试框架给予用户灵活的设置方式去选择需要的日志种类。在各种日志中,不仅将实际测试结果保留在了 “ .out ” 日志文件中,还将 “ .diff ” 留给用户。“ .diff ” 文件能帮助测试人员更快地发现期待值与实际测试结果的差别,以更快发现测试失败的问题所在。

然而,在 PHP 官方自动化测试框架中也存在一些对于用户来说不完美的地方,比如:在测试异常的情况下,不能输出完整的测试总结报告。如果测试人员需要完成一个长达十个小时的测试,而测试在第九小时发生了异常,但测试却无法给出所有跑过的测试的测试报告,这将是很恼人的。所以在自动化测试框架的实现中要考虑好测试异常情况下的处理。

结束语

PHP 官方自动化测试方法给黑盒测试人员提供了一个很好的自动化测试框架的范例。测试人员不仅可以利用 PHPT 测试脚本完善对 PHP 的测试,更可以通过学习这个范例构建自己的自动化测试框架和工具,让测试的自动化更好地服务于测试工作。


火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织