如何在PHP中模拟php://输入?

时间:2016-07-05 07:01:46

标签: php unit-testing

我正在为我的PHP项目编写一个单元测试,

单元测试是模拟php://input数据

我读了这本手册,它说:

  

php:// input是一个只读流,允许您读取原始数据   来自请求机构。

如何模拟php://input,或在我的PHP中编写请求正文?

这是我的源代码和单元测试,两者都是简化

来源

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;
    }

    //...

单元测试

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']);
}

5 个答案:

答案 0 :(得分:9)

请参阅vfsStream packagethis SO question and answers

基本上,您需要参数化读取数据以接受路径的服务:

var ctx = document.getElementById("myChart").getContext("2d");

var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: [
      "a",
      "b",
    ],
    datasets: [{
      data: [10, 20],
      backgroundColor: [
        "#DA1A32",
        "#F0FFFF"
      ],
      borderWidth: false,
      borderColor: false
    }]
  },
  options: {
    cutoutPercentage: 0.9
  }
});

然后,在测试中,提供vfsStream流路径:

public function __construct($path)
{
    $data = file_get_contents($path); // you might want to use another FS read function here
}

在您的代码中,您将按常规提供\vfsStreamWrapper::register(); \vfsStream::setup('input'); $service = new Service('vfs://input')

答案 1 :(得分:6)

使用测试双

考虑到问题中的代码,最简单的解决方案是重构代码:

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');
    }

并使用测试双:

class KoruTestDouble extends Koru
{
    protected static $inputStream;

    public static function setInputStream($input = '')
    {
        static::$inputStream = $input;
    }

    protected static function getInputStream()
    {
        return static::$inputStream;
    }
}

然后测试方法使用测试double,而不是类本身:

function testBuildInput()
{
    KoruTestDouble::setInputStream('test1=foobar&test2=helloWorld');

    $expected = ['test1' => 'foobar', 'test2' => 'helloWorld'];
    $result = KoruTestDouble::build();

    $this->assertSame($expected, $result, 'Stuff be different');
}

尽可能避免使用静态类

问题中的场景的大多数困难都是由静态类方法的使用引起的,静态类使测试变得困难。如果可能的话,可以避免使用静态类,并使用允许使用mock objects解决同类问题的实例方法。

答案 2 :(得分:2)

这种极端分解没有任何好处,导致代码非常脆弱。您的测试应该表达您的接口的期望,而不是您提供给他们的数据:PHP在以后的某个版本中是否真的无法自由返回["test2"=>"helloWorld","test1"=>"foobar"]?如果你的代码被破坏了吗?你认为你正在测试什么

我认为你过度复杂了。

$a->doit应将$input作为参数,而不是将Koru::build作为其初始化的一部分。然后,您可以测试$a->doit,而不是测试parse_str

如果你坚持按下这个例子,那么Koru::build需要采用'php://input'的参数 - 这通常被称为依赖注入,你可以告诉你的函数他们需要知道的一切。然后,当你想要"测试"事情,你可以简单地传递一些其他文件(或例如data url)。

答案 3 :(得分:1)

使用Kahlan,您可以像这样直接修补file_get_contents函数:

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'
        ]);

    });

});

答案 4 :(得分:1)

使用Zend \ Diactoros \ Stream

https://zendframework.github.io/zend-diactoros/usage/

$_POST['foo'] = 'bar';
use Zend\Diactoros\ServerRequestFactory;
$psrRequest = ServerRequestFactory::fromGlobals();
var_dump($psrRequest->getParsedBody()); // foo => bar
var_dump($_POST); // foo => bar

更多信息https://laracasts.com/discuss/channels/general-discussion/psr-7?page=1