0

我继承了一个 Zend Framework Web 应用程序,它既不是面向对象的,也不是在很多方面都写得很好,而且它带有零测试。因此,虽然我很欣赏单元测试的价值,并且会在我添加或重写代码时编写它们,但具有代码覆盖率的功能测试似乎是最好的起点。它也是了解应用程序如何工作的绝佳学习工具。这是 Zend Framework 1.11,Matthew Weier O'Phinney 已经声明 Zend_Test 仅适用于 PHPUnit 到 3.4。所以我安装了最新的3.4.15。我已经验证我可以扩展 Zend_Test_PHPUnit_ControllerTestCase 并以这种方式对控制器进行单元测试。

对于通过 Selenium 进行的功能测试,我在让代码覆盖工作时遇到了一些麻烦。事实上,我怀疑根据 Selenium 服务器的工作方式获取覆盖数据在技术上是否可行,但在PHPUnit_Selenium Code Coverage Work 的帮助下,它是否可行?PHPUnit_Selenium 代码覆盖是否有效?我终于让它与我的应用程序一起工作。能够看到代码的哪些部分被不同的请求击中真是太好了!

但我现在看到的问题是处理 POST 请求的代码似乎没有被报告。

例如,在控制器中,我有一个 contactAction 方法,其代码如下:

$this->view->form = $form;
 if ($this->getRequest()->isPost()) {
   if ($form->isValid($this->getRequest()->getPost())) {
     $values = $form->getValues();

这是用于联系我们的表格。在 Selenium 测试中,我打开 url,输入消息,单击提交按钮,然后等待页面加载。当我运行测试时,我可以在浏览器中看到这种情况,并收到带有由 selenium 键入的消息的电子邮件,所以我确定表单发布了有效数据,并且后两行确实执行了。但是在覆盖率报告中,前两行是绿色的,后两行是橙色的。

我检测了 prepend.php 和 append.php 脚本来记录以下信息:

     "(File: " . __FILE__ . "): REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD'] . "\n" .
     "REQUEST_URI: " . $_SERVER['REQUEST_URI'] . "\n" .
     "_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_COOKIE['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .
     "_GET['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_GET['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .
     "_POST['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_POST['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .

一件奇怪的事情是,只有 prepend 脚本会被记录,而不是 append 脚本。我不知道为什么,但它似乎不会影响覆盖数据(至少对于 GET 请求)。我看到的唯一可以解释的事情是 Zend MVC 应用程序是否以 exit() 结尾?那有意义吗?

以下是与我们联系互动记录的内容:

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: GET
REQUEST_URI: /index/contact
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '1'
_GET['PHPUNIT_SELENIUM_TEST_ID']: ''
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: GET
REQUEST_URI: /index/contact
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '1'
_GET['PHPUNIT_SELENIUM_TEST_ID']: ''
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: POST
REQUEST_URI: /index/contact
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '1'
_GET['PHPUNIT_SELENIUM_TEST_ID']: ''
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: GET
REQUEST_URI: /default/index/contact
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '1'
_GET['PHPUNIT_SELENIUM_TEST_ID']: ''
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: GET
REQUEST_URI: /default/index/contact
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '1'
_GET['PHPUNIT_SELENIUM_TEST_ID']: ''
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

(File C:\xampp\htdocs\myapp\public\prepend.php): REQUEST_METHOD: GET
REQUEST_URI: /phpunit_coverage.php?PHPUNIT_SELENIUM_TEST_ID=a85030b0bcdb0460bfb17a83a373d6b5
_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: ''
_GET['PHPUNIT_SELENIUM_TEST_ID']: '1'
_POST['PHPUNIT_SELENIUM_TEST_ID']: ''
extension_loaded('xdebug'): '1'

如您所见,GET 请求被记录两次,而 POST 只记录一次。也许这与似乎只有 GET 请求被覆盖而 POSTS 没有被覆盖的事实有关?但我真的不理解这种行为的几个方面,我不知道该怎么做才能帮助解决这个问题。

任何人都知道为什么我没有得到 POST 请求的报道,或者下一步可能是什么来追踪它?

4

1 回答 1

0

在这个问题中,我推测如果 Zend 框架正在调用 exit(),这可能解释了为什么 append.php 脚本从未被记录。于是我在 ZF 库的整个源代码中搜索了对 exit() 的调用,结果只有三个:

library/Zend/Controller/Action/Helper/Redirector.php:        exit();
library/Zend/Oauth/Consumer.php:        exit(1);
library/Zend/OpenId.php:            exit();

在这种情况下,没有使用 Oauth 或 OpenId。但正如我在评论中指出的那样,POST 请求代码在登录失败并且重新显示表单时显示覆盖范围,但在成功并且重定向到欢迎页面时却没有。所以重定向器看起来像是检查的地方。

对于重定向,本应用的动作例程中的代码统一使用:

$this->_redirect("some-url");

而且我从未研究过它是如何工作的或任何选项,因为它从来都不是问题。但是由于它可能导致覆盖问题,我阅读了它的文档并浏览了 Zend 源代码。我发现有一个简单的选项可以导致重定向不调用 exit()。所以我把有问题的改成:

return $this->_redirect("some-url", array('exit'=>false));

瞧,POST 请求的缺失代码覆盖率出现了!

现在我想知道我是否应该覆盖 _redirect 以便它始终抑制退出调用,或者我是否应该只在测试环境中这样做。我猜想在重定向时立即终止脚本可能会节省一些生产周期。

这样主要的问题就解决了。但谜团仍然是,我在 append.php 脚本中输入的日志代码仍然没有产生任何输出—— prepend.php 脚本总是产生它的日志输出,但从不产生 append.php。正是 append.php 没有输出让我跟踪了 ZF 中的 exit() 调用,但似乎还有其他一些原因导致没有产生输出。

我终于也想出了这个。这只是附加脚本中的一个错字。我简直不敢相信,因为我“知道”我添加到 append.php 的日志记录代码与 prepend.php 中的代码相同,从 prepend 到 append 进行了复制粘贴。但显然我在副本上选择了胖手指,并错过了消息文本的最后一行。当我终于看到丢失的行时,我的第一反应是“这不可能,我应该得到一个语法错误,而不仅仅是缺少输出。” 这是我预期的代码:

$msg ="\n(File " . __FILE__ . "): REQUEST_METHOD: " . $_SERVER['REQUEST_METHOD'] . "\n" .
         "REQUEST_URI: " . $_SERVER['REQUEST_URI'] . "\n" .
         "_COOKIE['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_COOKIE['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .
         "_GET['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_GET['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .
         "_POST['PHPUNIT_SELENIUM_TEST_ID']: '" . print_r(isset($_POST['PHPUNIT_SELENIUM_TEST_ID']), 1) . "'\n" .
         "extension_loaded('xdebug'): '". print_r(extension_loaded('xdebug'), 1) . "'\n";
file_put_contents($GLOBALS['PHPUNIT_COVERAGE_DATA_DIRECTORY'] . "/msg.log", $msg, FILE_APPEND);

但我不小心错过了 $msg 作业的最后一行。因此,当我意识到它丢失了,并在调用 file_put_contents() 之前看到了悬空的字符串连接运算符时,起初我很困惑,我没有收到语法错误。但是当然 php 很乐意使用 $msg 的旧值(在 prepend 脚本中分配的值)调用 file_put_contents(),并将其结果(写入的字节数)附加到分配给 $msg 的字符串(然后从未使用过)!当您准确地看到代码时,实际上并没有什么令人惊讶的,但是当您假设您的正确代码的复制粘贴不可能出错时,您的头脑会变得模糊不清!

于 2013-03-03T20:07:12.513 回答