如何在 PHP 中模拟 php://input?

2022-08-30 16:11:06

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

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

我读了手册,上面写着:

php://input 是一个只读流,允许您从请求正文中读取原始数据。

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


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

资料来源

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

答案 1

使用测试双精度

给定问题中的代码,最简单的解决方案是重构代码:

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

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

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

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

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

如果可能,避免使用静态类

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


答案 2

请参阅 vfsStream 包此 SO 问题和解答

基本上,您可能希望对读取数据的服务进行参数化以接受路径:

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

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

\vfsStreamWrapper::register();
\vfsStream::setup('input');

$service = new Service('vfs://input') 

在您的代码中,您将照常提供。php://input


推荐