I'm writing an unit test for my PHP project,
the unit test is to simulate a php://input
data,
and I read the manual, it says:
php://input is a read-only stream that allows you to read raw data from the request body.
How do I simulate the php://input
, or write the request body in my PHP?
Here's my source code and unit test, both are simplified.
Source:
class Koru
{
static function build()
{
// This function will build an array from the php://input.
parse_str(file_get_contents('php://input'), $input);
return $input;
}
//...
Unit Test:
function testBuildInput()
{
// Trying to simulate the `php://input` data here.
// NOTICE: THIS WON'T WORK.
file_put_contents('php://input', 'test1=foobar&test2=helloWorld');
$data = Koru::build();
$this->assertEquals($data, ['test1' => 'foobar',
'test2' => 'helloWorld']);
}
See vfsStream package and this SO question and answers.
Basically, you would want to parametrize your service that reads data to accept a path:
public function __construct($path)
{
$data = file_get_contents($path); // you might want to use another FS read function here
}
And then, in a test, provide an vfsStream stream path:
\vfsStreamWrapper::register();
\vfsStream::setup('input');
$service = new Service('vfs://input')
In your code you would provide php://input
as per usual.
Given the code in the question, the simplest solution is to restructure the code:
class Koru
{
static function build()
{
parse_str(static::getInputStream(), $input);
return $input;
}
/**
* Note: Prior to PHP 5.6, a stream opened with php://input could
* only be read once;
*
* @see http://php.net/manual/en/wrappers.php.php
*/
protected static function getInputStream()
{
return file_get_contents('php://input');
}
And use a test double:
class KoruTestDouble extends Koru
{
protected static $inputStream;
public static function setInputStream($input = '')
{
static::$inputStream = $input;
}
protected static function getInputStream()
{
return static::$inputStream;
}
}
The test method then uses the test double, not the class itself:
function testBuildInput()
{
KoruTestDouble::setInputStream('test1=foobar&test2=helloWorld');
$expected = ['test1' => 'foobar', 'test2' => 'helloWorld'];
$result = KoruTestDouble::build();
$this->assertSame($expected, $result, 'Stuff be different');
}
Most of the difficulties with the scenario in the question are caused by the use of static class methods, static classes make testing hard. If at all possible avoid the use of static classes and use instance methods which allows solving the same sort of problem using mock objects.
This sort of extreme decomposition gains nothing and leads very brittle code. Your tests should express the expectations of your interfaces, and not the data you've supplied them with: Is PHP truly not free to return ["test2"=>"helloWorld","test1"=>"foobar"]
in some future version? Is your code broken if it does? What exactly do you think you are testing?
I think you're overcomplicating this.
$a->doit
should take $input
as an argument and not call Koru::build
as part of its initialisation. Then you can test $a->doit
instead of testing parse_str.
If you insist on pressing on this example, then Koru::build
needs to take an argument of 'php://input'
– this is often called dependency injection, where you tell your functions everything they need to know. Then, when you want to "test" things, you can simply pass in some other file (or e.g. a data url).
With Kahlan you can monkey patch the file_get_contents
function directly like so:
use My\Name\Space\Koru;
describe("::build()", function() {
it("parses data", function() {
allow('file_put_contents')->toBeCalled()->andRun(function() {
return 'test1=foobar&test2=helloWorld';
});
expect(Koru::build())->toBe([
'test1' => 'foobar',
'test2' => 'helloWorld'
]);
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With