【问题标题】:PHPUnit Selenium RC test coverage works for GET but not POST requests?PHPUnit Selenium RC 测试覆盖适用于 GET 但不适用于 POST 请求?
【发布时间】:2013-02-12 23:01:48
【问题描述】:

我继承了一个 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 服务器的工作方式,我怀疑获取覆盖数据在技术上是否可行,但在 Does PHPUnit_Selenium Code Coverage Work?Does PHPUnit_Selenium Code Coverage Work? 的帮助下,我终于让它与我的应用程序一起工作。能够看到代码的哪些部分被不同的请求击中真是太好了!

但我现在看到的问题是处理 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" .

一个奇怪的事情是,只有前置脚本被记录,而不是附加脚本。我不知道为什么,但它似乎不会影响覆盖数据(至少对于 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 请求的报道,或者下一步可能是什么来追踪它?

【问题讨论】:

  • 我刚刚输入了一个答案,将我的 XDebug 版本从 2.1.0 更新到 2.1.2 解决了这个问题。但是我删除了它,因为进一步的测试表明,使代码显示为所涵盖的是测试环境的更改:密码已更改,因此登录失败。因此,成功登录后,它会重定向,并且不会覆盖 POST 请求。如果登录失败,它会显示 POST 的覆盖范围,并重新显示表单。 XDebug 的版本没有什么不同。这是 WinXP SP3,PHP 5.3.1。

标签: zend-framework selenium phpunit code-coverage


【解决方案1】:

在这个问题中,我推测如果 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 的字符串(然后从未使用过)!当您准确地看到代码时,实际上并不令人惊讶,但当您假设您的正确代码的复制粘贴不可能出错时,您的头脑一片混乱!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    • 2022-01-21
    • 2017-08-06
    • 1970-01-01
    • 1970-01-01
    • 2020-07-19
    • 2020-07-07
    相关资源
    最近更新 更多