在测试中发送标头时,PHPUnit Web测试错误

时间:2018-02-26 13:28:48

标签: php rest symfony phpunit

我在symfony中编写了一个REST API。它接受Json请求并生成带有http://jsfiddle.net/r6KYu/格式的pdf作为响应。

我想编写一个phpunit web测试,它将检查响应状态和标题数据。

 <?php
/*
Plugin Name: Zuzu Viral Reactions
Plugin URI: http://tiguandesign.com/
Description: Simple WordPress reactions plugin for viral stories.
Author: Tiguan
Author URI:  http://tiguandesign.com/
Licence: GPLv2
Version: 1.0
Stable Tag: 1.0
*/

  class Zuzu_Viral_React {
      // $reactions = array( 'like','love', 'win', 'cute', 'lol', 'omg', 'wtf', 'fail' );

    function __construct() {
        $this->defaults = array(
            'like' => "Like",
            'love' => "LOVE",
            'win' => "Win",
            'cute' => "Cute",
            'lol' => "LOL",
            'omg' => "OMG",
            'wtf' => "WTF",
            'fail' => "Fail",
            'boxtitle' => "Your Reaction"
          );
          add_action('the_content', array($this,'addContent'));
        add_action('the_excerpt', array($this, 'zvrdisablePlugin'));
        add_action('admin_menu', array($this, 'addMenu'));
        add_action( 'admin_init', array($this, 'registerSettings'));
          add_action( 'wp_ajax_zvr_react', array($this,'react'));
        add_action( 'wp_ajax_nopriv_zvr_react', array($this,'react' ));
        add_action('wp_enqueue_scripts', array($this,'addStylesAndScripts'));
        add_action( 'load-post.php', array($this, 'initMetaBox'));
        add_action( 'load-post-new.php', array($this, 'initMetaBox'));
        add_shortcode( 'zvr_reactions', array($this, 'shortCode') );
        add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), array($this, 'addSettingsLink' ));
    }

      function addSettingsLink ( $links ) {
        $link = array('<a href="' . admin_url( 'options-general.php?page=zvr_options' ) . '">Settings</a>');
        return array_merge( $links, $link );
    }
    function initMetaBox() {
        add_action( 'add_meta_boxes', array($this, 'addMetaBox'));
        add_action( 'save_post', array($this, 'savePostMeta'), 10, 2 );
    }
      function savePostMeta($post_id, $post) {
        if ( !isset( $_POST['zvr_enable_meta_nonce'] ) || !wp_verify_nonce( $_POST['zvr_enable_meta_nonce'], basename( __FILE__ ) ) )
        return $post_id;
          $post_type = get_post_type_object( $post->post_type );
          if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
        return $post_id;
          $meta_value = ( isset( $_POST['zvr_enable'] ) ? sanitize_html_class( $_POST['zvr_enable'] ) : '' );
        if (empty($meta_value)) {
            $meta_value = "off";
        }
        update_post_meta( $post_id, 'zvr_enable', $meta_value );
    }
      function addMetaBox() {
        add_meta_box('zvr-enable-on-post', 'Zuzu Viral Reactions', array($this, 'renderMetaBox'), 'post', 'normal', 'default');
    }
      function renderMetaBox() {
        $options = get_option( 'zvr_settings' );
        $enable = isset($options['zvr_auto_enable']) ? $options['zvr_auto_enable']: 'on';
        $post_id = get_the_ID();
        $meta_enable = get_post_meta( $post_id, 'zvr_enable', true );
        if (!empty($meta_enable)) {
            $enable = $meta_enable;
        }
        wp_nonce_field( basename( __FILE__ ), 'zvr_enable_meta_nonce' );
        ?>
          <label><input type="checkbox" name="zvr_enable" id="zvr-enable" <?php checked($enable, 'on')?>>Enable reactions on this post</label>
        <?php
    }
      function addMenu() {
        add_options_page('Zuzu Reaction Settings', 'Zuzu Reaction', 'manage_options', 'zvr_options', array($this, 'renderOptionsPage'));
    }
      function registerSettings() {
        register_setting('zvr_options', 'zvr_settings');
        add_settings_section( 'zvr_enable', '', array($this, 'renderEnableGuide'), 'zvr_options' );
        add_settings_field(    'zvr_options-auto-enable-on',    'Show buttons on posts', array($this, 'renderRadio'), 'zvr_options', 'zvr_enable', array('value' => 'on'));
        add_settings_field(    'zvr_options-auto-enable-off',    "Don't show buttons on posts", array($this, 'renderRadio'), 'zvr_options', 'zvr_enable', array('value' => 'off'));
        add_settings_section( 'zvr_content', '', array($this, 'renderContent'), 'zvr_options' );
        add_settings_section( 'zvr_share_translations', 'Reaction box title', array($this, 'renderReactionTranslations'), 'zvr_options');
        add_settings_field(    'zvr_options-boxtitle',    'Your Reaction', array($this, 'renderField'), 'zvr_options', 'zvr_share_translations', array('label' => 'boxtitle'));
        add_settings_section( 'zvr_translations', 'Reactions Text', array($this, 'renderReactionTranslations'), 'zvr_options' );
        add_settings_field(    'zvr_options-like',    'Like', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'like'));
        add_settings_field(    'zvr_options-love',    'Love', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'love'));
        add_settings_field(    'zvr_options-win',    'Win', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'win'));
        add_settings_field(    'zvr_options-cute',    'Cute', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'cute'));
        add_settings_field(    'zvr_options-lol',    'LOL', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'lol'));
        add_settings_field(    'zvr_options-omg',    'OMG', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'omg'));
        add_settings_field(    'zvr_options-wtf',    'WTF', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'wtf'));
        add_settings_field(    'zvr_options-fail',    'Fail', array($this, 'renderField'), 'zvr_options', 'zvr_translations', array('label' => 'fail'));

      }

    function shortCode() {
        $options = get_option('zvr_settings');
        return $this->renderPlugin($options);
    }
      function renderField($args) {
        $label = $args['label'];
        $options = get_option('zvr_settings');
        $value = isset($options['zvr_'.$label]) ? $options['zvr_'.$label]: $this->defaults[$label];
        echo "<input type='text' name='zvr_settings[zvr_$label]' value='".esc_attr($value)."'>";
    }
      function renderContent() {
        ?>
              <div style="border-top: 1px solid #bbb; width: 100%; padding: 30px 0; margin: 30px 0; border-bottom: 1px solid #bbb;">
                  <h3>Adding reactions manually (short code)</h3>
                <ol>
                    <li>You can use shortcode <code>[zvr_reactions]</code> within post or page text.</li>
                    <li>You can add <code>if (function_exists('zvr_reactions')) { zvr_reactions() }</code> into your templates.</li>
                </ol>
              </div>

        <?php
    }
    function zvrdisablePlugin($excerpt) {
        $pattern = '/zvr.*/i';
        return preg_replace($pattern, '', $excerpt);
    }

    function renderRadio($args) {
        $options = get_option( 'zvr_settings' );
        $value = $args['value'];
        $set_value = isset($options['zvr_auto_enable']) ? $options['zvr_auto_enable']: 'on';
        ?>
        <input type='radio' name='zvr_settings[zvr_auto_enable]' <?php checked( $set_value, $value ); ?> value='<?php echo $value ?>'>
        <?php
    }
       function renderReactionTranslations() {
         echo "";
     }
       function renderEnableGuide() {
         ?>

         <?php
     }
      function renderOptionsPage() {
        ?>
          <form action='options.php' method='post'>
              <p><h1>Zuzu Viral Reaction Settings</h1></p><br />
              <p>Select the default setting for Zuzu Viral Reaction visibility. You can override this setting for each post in the post editor.</p>
              <?php
            settings_fields( 'zvr_options' );
              do_settings_sections( 'zvr_options' );
            ?>

            <?php submit_button(); ?>
          </form>
          <?php
    }

    function addContent($content) {
        $options = get_option('zvr_settings');
        $show_on_every_post = isset($options['zvr_auto_enable']) ? $options['zvr_auto_enable'] : 'on';
        $post_id = get_the_ID();
        $enabled = get_post_meta( $post_id, 'zvr_enable', true );
        if (!is_page() && ($enabled=="on" || (empty($enabled) && $show_on_every_post=='on'))) {
            $plugin = $this->renderPlugin($options);
            $content .= $plugin;
        }
        return $content;
    }
      function renderPlugin($options) {
        $post_id = get_the_ID();
        $post_url = get_permalink($post_id);
        $label_like =isset($options['zvr_like']) ? $options['zvr_like']: $this->defaults['like'];
        $label_love =isset($options['zvr_love']) ? $options['zvr_love']: $this->defaults['love'];
        $label_win =isset($options['zvr_win']) ? $options['zvr_win']: $this->defaults['win'];
        $label_cute =isset($options['zvr_cute']) ? $options['zvr_cute']: $this->defaults['cute'];
        $label_lol =isset($options['zvr_lol']) ? $options['zvr_lol']: $this->defaults['lol'];
        $label_omg =isset($options['zvr_omg']) ? $options['zvr_omg']: $this->defaults['omg'];
        $label_wtf =isset($options['zvr_wtf']) ? $options['zvr_wtf']: $this->defaults['wtf'];
        $label_fail =isset($options['zvr_fail']) ? $options['zvr_fail']: $this->defaults['fail'];
        $label_boxtitle =isset($options['zvr_boxtitle']) ? $options['zvr_boxtitle']: $this->defaults['boxtitle'];

          ob_start() ?>
            <div id="zuzu_viral_reactions">
                <span style="display:none">zvr</span>
                <div class="zvr-reaction-title"><?php echo $label_boxtitle ?></div>
                <ul data-post-id="<?php echo $post_id ?>">
                  <li class="animated" data-reaction="like" <?php echo $this->getClass("like", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f44d.svg' ?>" /><em><?php echo $label_like ?></em><span><?php echo $this->getAmount("like",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="love" <?php echo $this->getClass("love", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f60d.svg' ?>" /><em><?php echo $label_love ?></em><span><?php echo $this->getAmount("love",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="win" <?php echo $this->getClass("win", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f61c.svg' ?>" /><em><?php echo $label_win ?></em><span><?php echo $this->getAmount("win",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="cute" <?php echo $this->getClass("cute", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f917.svg' ?>" /><em><?php echo $label_cute ?></em><span><?php echo $this->getAmount("cute",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="lol" <?php echo $this->getClass("lol", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f632.svg' ?>" /><em><?php echo $label_lol ?></em><span><?php echo $this->getAmount("lol",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="omg" <?php echo $this->getClass("omg", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f631.svg' ?>" /><em><?php echo $label_omg ?></em><span><?php echo $this->getAmount("omg",$post_id) ?></span></a></li>
                     <li class="animated" data-reaction="wtf" <?php echo $this->getClass("wtf", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f914.svg' ?>" /><em><?php echo $label_wtf ?></em><span><?php echo $this->getAmount("wtf",$post_id) ?></span></a></li>
                  <li class="animated" data-reaction="fail" <?php echo $this->getClass("fail", $post_id) ?> ><a href="javascript<b></b>:void(0)"><img class="animated" src="<?php echo trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/img/1f915.svg' ?>" /><em><?php echo $label_fail ?></em><span><?php echo $this->getAmount("fail",$post_id) ?></span></a></li>
                </ul>
                  <div style="clear: both;"></div>
             </div>
          <?php
        $plugin = ob_get_contents();
        ob_clean();
        return $plugin;
    }
      function getClass($reaction, $post_id) {
          $clicked = isset($_COOKIE["zvr_reacted_".$reaction."_".$post_id]);
          return ($clicked ? 'class="clicked"':'');
    }
      function getAmount($reaction, $post_id) {
        $meta_key = "zvr_reaction_".$reaction;
        $amount = get_post_meta($post_id, $meta_key, true) ? get_post_meta($post_id, $meta_key, true) : 0;
        return $amount;
    }
      function react() {
        if (isset($_POST["postid"])) {
            $post_id = $_POST["postid"];
            $reaction = $_POST["reaction"];
            $unreact = $_POST["unreact"];
        }
         $amount = $this->getAmount($reaction, $post_id);
        if (isset($unreact) && $unreact === "true") {
            unset($_COOKIE['zvr_reacted_'.$reaction.'_'.$post_id]);
            setcookie('zvr_reacted_'.$reaction.'_'.$post_id, '', time() - 3600, "/");
            $amount = (int) $amount - 1;
            if ($amount >=0) {
                echo "Amount: ".$amount." ";
                update_post_meta($post_id, "zvr_reaction_".$reaction, $amount);
            }
        }
        else {
            setcookie('zvr_reacted_'.$reaction.'_'.$post_id, $reaction, time() + (86400 * 30), "/");
            $amount = (int) $amount + 1;
            if ($amount >=0) {
                echo "Amount: ".$amount." ";
                update_post_meta($post_id, "zvr_reaction_".$reaction, $amount);
            }
        }
        return;
    }
      function addStylesAndScripts() {
        wp_enqueue_style( 'zvr-font', 'https://fonts.googleapis.com/css?family=Open+Sans' );
        wp_enqueue_style( 'zvr-style', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/css/zvr-styles.css', array(), "1.0.3" );
        wp_enqueue_script( 'zvr-script', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'assets/js/zvr-script.js', array( 'jquery' ), "1.0.3" );
        $localize = array(
            'ajax_url' => admin_url( 'admin-ajax.php' ),
        );
          wp_localize_script( 'zvr-script', 'zvr_data', $localize );
    }
  }
  function zvr_reactions() {
    // Call from templates
    // if (function_exists('zvr_reactions')) { zvr_reactions() }
    $zvr = new Zuzu_Viral_React();
    $options = get_option('zvr_settings');
    echo $zvr->renderPlugin($options);
}
  new Zuzu_Viral_React();

问题是,当运行测试时,PHPUnit已经启动输出头,然后我的控制器有问题生成PDF:

public function test_getCertificate()
{
    $client = $this->getClient(['username'=>'user','password'=>'xxxxxx']);

    $client->request(
        'GET',
        '/api/v1/certificate',
        array(),
        array(),
        array('CONTENT_TYPE' => 'application/json'),
        null
    );

    $this->assertEquals(200, $client->getResponse()->getStatusCode());

    $this->assertTrue(
        $client->getResponse()->headers->contains(
            'Content-Type',
            'application/x-download'
        )
    );

    $this->assertTrue(
        $client->getResponse()->headers->contains(
            'Content-Disposition',
            'attachment; filename="el_certificate.pdf"'
        )
    );
}

header()由库代码调用,我无法修改。当我尝试使用ob_end_clean()清理测试脚本中的输出缓冲区或使用ob_start()启动新缓冲区时获取异常:

Uncaught PHP Exception Exception: "FPDF error: Some data has already been output, can't send PDF file"

版本Uncaught PHP Exception PHPUnit_Framework_Error_Warning: "Cannot modify header information - headers already sent by (output started at /usr/share/php/PHPUnit/Util/Printer.php:134)" PHP 7.2.2-3+ubuntu16.04.1+deb.sury.org+1

有关如何处理这种情况的任何建议吗?

修改 我试图在一个单独的过程中运行这个测试:

PHPUnit 5.1.3

然后,我收到了错误:

/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function test_getCertificate()
{
    //...
}

1 个答案:

答案 0 :(得分:1)

好的,我终于弄清楚发生了什么。

如果PHPUnit应该测试一些发出标题的代码(例如生成文件),那么测试应该在一个单独的进程中运行。这可以实现using annotation

/**
 * @runInSeparateProcess
 * @preserveGlobalState disabled
 */
public function test_getCertificate()
{
    //...
}

就我而言,JMSDiExtraBundle产生错误,因为它将它包装在自己的代理对象中。如果不希望JMSDiExtra为doctrine生成代理类,则可以添加以下配置:

jms_di_extra:
    doctrine_integration: false

之后,测试可以在单独的过程中正常运行而不会出现标题输出问题。