7

I'm new to PHPUnit testing framework.

as we know that move_uploaded_file() function of PHP will not work until the file is uploaded via http POST method

So, the Question is how to simulate this in PHP command line

Note: using selenium we can simulate webform.. but i need another alternative.

edorian
  • 38,542
  • 15
  • 125
  • 143
khizar ansari
  • 1,476
  • 2
  • 18
  • 29

5 Answers5

8

You basically need to make your code more testable. Break it down so you can test the simple act of uploading a file through HTTP separately from the rest of the code. The primary use of move_uploaded_file is to put in an extra security stop so you cannot be tricked into moving some other file, move_uploaded_file simply makes sure that the file was uploaded in the same request and then moves it. You can simply move the file using rename as well. As such, break your application down to have one Request object which represents and encapsulates the current HTTP request, including making it check uploaded files using is_uploaded_file. Once that's validated, you can use rename instead of move_uploaded_file. In your tests you can then mock the Request object and test your other code.

You can also simply make move_uploaded_file mockable, for example like this:

class Foo {

    public function do() {
        ...
        $this->move_uploaded_file($from, $to);
        ...
    }

    protected function move_uploaded_file($from, $to) {
        return move_uploaded_file($from, $to);
    }

}

In your tests you can extend/mock the class and override Foo::move_uploaded_file to always return true, for instance.

deceze
  • 510,633
  • 85
  • 743
  • 889
3

This is where the PHP Testing Framework (PHPT) comes in. It is installed with PEAR and allows you to feed HTTP requests, I/O operations, file uploads, etc. to a PHP script by writing a .phpt file and executing it with run-phpt or pear run-tests <file.phpt>. You can also run them with PHPUnit using PhptTestCase and PhptTestSuite.

To simulate a file upload you need to use the --POST_RAW-- section with the same data the browser sends. This is the example from the PHP QAT site:

Important: Unfortunately, the example on their site fails without any explanation.

is_uploaded_file.phpt

--TEST--
is_uploaded_file() function
--CREDITS--
Dave Kelsey <d_kelsey@uk.ibm.com>
--SKIPIF--
<?php if (php_sapi_name()=='cli') die('skip'); ?>
--POST_RAW--
Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="field1"

Joe Blow
--AaB03x
content-disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain

abcdef123456789
--AaB03x--
--FILE--
<?php
// uploaded file
var_dump(is_uploaded_file($_FILES['pics']['tmp_name']));

// not an uploaded file
var_dump(is_uploaded_file($_FILES['pics']['name']));

// not an uploaded file
var_dump(is_uploaded_file('random_filename.txt'));

// not an uploaded file
var_dump(is_uploaded_file('__FILE__'));

// Error cases
var_dump(is_uploaded_file());
var_dump(is_uploaded_file('a', 'b'));

?>
--EXPECTF--
bool(true)
bool(false)
bool(false)
bool(false)

Warning: is_uploaded_file() expects exactly 1 parameter, 0 given in %s on line %d
NULL

Warning: is_uploaded_file() expects exactly 1 parameter, 2 given in %s on line %d
NULL

Running

pear run-tests --cgi=PHPCGI is_uploaded_file.phpt

Manuel Pichler wrote up an example in a blog post, but it fails as well.

Community
  • 1
  • 1
David Harkness
  • 35,992
  • 10
  • 112
  • 134
2

If you want to use PHPUnit to test file upload without mocking out the actual move_uploaded_file check you can use .phpt test cases, which PHPUnit can execute, so simulate a web request.

Basically you create a .phpt file that looks like this:

Standalone example: uploadTest.phpt

--TEST--
Example test emulating a file upload
--POST_RAW--
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfywL8UCjFtqUBTQn

------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain

Qafoo provides quality assurance support and consulting

------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="submit"

Upload
------WebKitFormBoundaryfywL8UCjFtqUBTQn--
--FILE--
<?php

var_dump(is_uploaded_file($_FILES['file']['tmp_name']));

?>
--EXPECT--
bool(true)

run using phpunit uploadTest.phpt


For the full explanation check out the blog post describing the details:

http://qafoo.com/blog/013_testing_file_uploads_with_php.html

Also: There is working sample code over at github


More verbose real world example:

--TEST--
Example test emulating a file upload
--POST_RAW--
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryfywL8UCjFtqUBTQn

------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain

Qafoo provides quality assurance support and consulting

------WebKitFormBoundaryfywL8UCjFtqUBTQn
Content-Disposition: form-data; name="submit"

Upload
------WebKitFormBoundaryfywL8UCjFtqUBTQn--
--FILE--
<?php
require __DIR__ . '/UploadExample.php';

$upload = new UploadExample('/tmp');
$upload->handle('file');

var_dump(file_exists('/tmp/example.txt'));
?>
--EXPECT--
bool(true)
Community
  • 1
  • 1
edorian
  • 38,542
  • 15
  • 125
  • 143
  • Does this example work for you? I cannot even get the simple `POST_RAW` example from qa.php.net to work, and there's no clue what the failure is. I'm running PHP 5.3.10-1 on Xubuntu. – David Harkness Nov 26 '12 at 21:06
  • @DavidHarkness I've edited the question with a standalone example that works nicely for me. 5.3 & 5.4 ubuntu using PHPUnit 3.6 & 3.7. – edorian Nov 26 '12 at 21:12
  • PHPUnit 3.7.9 skips the test. Is there any way to determine why it's being skipped? – David Harkness Nov 26 '12 at 21:20
  • @DavidHarkness if you're still interested, 3 years later...I was not able to run tests which use post_raw unless I specify the CGI parameter like this `pear run-tests --cgi=C:\xampp\php\php-cgi.exe C:\xampp\htdocs\PC_administration_interface\Controler\Test\Test_interface_builder/invalid_file.phpt` – Jonathan Parent Lévesque Oct 07 '15 at 19:24
1

Personally, I like the answer given by deceze above.

However, another possibility is the use of namespaces.

The code for this is on Github.

Community
  • 1
  • 1
untill
  • 1,513
  • 16
  • 20
0

You can do file uploads from the command line with cURL:

curl --form input-name=@filename http://localhost/upload.php

with input-name being the name of the input field in the HTML normally used and filename being path and name of the file to be uploaded - remember to put them in quotes or escape special character if they contain some.

However, this obviously still requires running the script from a webserver and apparently that is not what you want, so see How can I write tests for file upload in PHP? for ways to test file uploads with PHPUnit.

Community
  • 1
  • 1
AndreKR
  • 32,613
  • 18
  • 106
  • 168