13

Having some issues using PHPUnit to test my controllers.

Code I was working on so far was implementing $_POST or other request variables:

$_SERVER['REQUEST_METHOD'] = 'POST';
$_POST = array(
    'test' => true
);

Most of tests worked perfectly this way until I run into methods that take uses of filter_input_array function:

$_SERVER['REQUEST_METHOD'] = 'POST';
$_REQUEST = $_POST = $GLOBALS['_POST'] = array(
    'test' => true
);

// ....

var_dump(filter_input_array(INPUT_POST));

NULL

I'm not willing to remove filter_input functions from not mine code, but I'm unable to make them working in tests.

Versionings:
PHP 5.5.9-1ubuntu4.9 (cli) (built: Apr 17 2015 11:44:57)
Apache/2.4.7 (Ubuntu)
PHPUnit 4.6.6 by Sebastian Bergmann and contributors.

Any help will be appreciated.

EDIT 2015.05.11

Setting $_SERVER with CONTENT_LENGTH and CONTENT_TYPE does not fix the problem. My version of PHP does not allow me to write to php://stdin in way its described in PHP 5.6.0 chagelog (or way I understand it), but file_put_contents(STDIN,..) succeed but does not work anyway.

Because it is a phpunit test, maybe there is some kind of annotation or phpunit.xml entry I don't know yet, that may fix this problem in php-cgi POST setting manner.

3
  • Your two examples assign $_POST differently. What are the values of the three variables $_REQUEST, $_POST and $GLOBALS['_POST'] before you do $_REQUEST = $_POST = $GLOBALS['_POST']? Commented May 17, 2015 at 10:24
  • Their empty arrays. As it is a phpunit test, it runs in command line (php-cli) so it is not even going though web server and no $_REQUEST-related vars should be set anyway. I was looking at unit tests of php itself, how they test filter_input_array but it did not helped me at all. Commented May 17, 2015 at 12:51
  • take a look at stackoverflow.com/questions/5655284/… it may help ? Commented May 22, 2015 at 14:49

4 Answers 4

13
+50

If the input to filter_input_array can only be set by the initial request, and not changed at run time, then the only way to still test it is to have a your base test proxy to another test script by making an HTTP request with the right POST data and processing the response.

main_test.php:

<?php
$data = array(
    'testname' => 'yourtestname',
    'some_post_var' => 'some_value'
);

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://localhost/proxy_test.php");
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);

curl_close($ch);

if ($response == 'pass') {
  // test passed
}

proxy_test.php

<?php
$test_name $_POST['testname']; // what test to run?
$response = run_test($test_name); // run test that uses filter_input_array
if ($response) {
    echo "pass"; // used by main_test.php to check if test passed
} else {
   echo "fail";
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thumbs up for suggesting curl. It's workaround, but does seem to work. Also, can you edit your proxy_test( $test_name =) ?
Looks like a way to do this. Will try. Unfortunately it probably won't provide code coverage in configuration I have curretly, but for now can live w/o that.
7

It seems like this is a limitation of PHP, filter_input_array() does not allow a $_POST array modified at runtime. See this bug for some more information. The workaround is probably to use one of the other filter functions and pass in the $_POST array yourself.

1 Comment

Seems like providen bug still exists, as function is returning NULL instead of false or empty array. Anyway, I think that it may be possible to run this test in phpunit, as this tool provides running scripts in separate processes, and I provided an SO answer that proofs that setting POST on script startup is possible. I just have not found a workaround for my case.
1

If none of the arguments is set, this function returns NULL, not an array of NULL values.

// No POST vars set in request

$_POST = array();
$args = array('some_post_var' => FILTER_VALIDATE_INT);
$myinputs = filter_input_array(INPUT_POST, $args);
var_dump($myinputs);

Expected Output: array(1) { ["some_post_var"]=> NULL } 

Actual Output: NULL

While filtering input arrays, be careful of what flags you set besides FILTER_REQUIRE_ARRAY. For example, setting the flags like so:

 <?php 
    $filter = array( 
    'myInputArr' => array('filter' => FILTER_SANITIZE_STRING, 
                          'flags' => array('FILTER_FLAG_STRIP_LOW', 'FILTER_REQUIRE_ARRAY')) 
    ); 

$form_inputs = filter_input_array(INPUT_POST, $filter); 
?> 

.. will result in a blank $form_inputs['myInputArr'] regardless of what $_POST['myInputArr'] contains.

1 Comment

Sorry if I can't follow, but how is this helping me with my question?
1

One approach to this is to use a helper method to run your filter_input_array inside of then mock this method during tests to use something else like filter_var_array.

For example this use case could be accomplished by doing:

class Data_Class {

    protected function i_use_filters() {
        return $this->filter_input_array();
    }


    protected function filter_input_array() {
        return filter_input_array( INPUT_POST );
    }
}

class Test_Class extends TestCase {

    public function test_filters() : void {
        $mock = $this->getMockBuilder( Data_Class::class )
                     ->setMethods( [ 'filter_input_array' ] )
                     ->getMock();
        $mock->method( 'filter_input_array' )
             ->willReturnCallback( function () {
                 if ( ! isset( $_POST ) ) {
                     return null;
                 }
                 return \filter_var_array( $_POST );
             } );

        $_POST['test'] = true;
        $this->assertTrue( $mock->i_use_filters()['test'] );
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.