我如何模拟php://input 在PHP中?
我正在为我的PHP项目编写一个单元测试 单元测试是模拟我如何模拟php://input 在PHP中?,php,unit-testing,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
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']);
}
单元测试:
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']);
}
见和
基本上,您希望将读取数据以接受路径的服务参数化:
public function __construct($path)
{
$data = file_get_contents($path); // you might want to use another FS read function here
}
然后,在测试中,提供vfstream流路径:
\vfsStreamWrapper::register();
\vfsStream::setup('input');
$service = new Service('vfs://input')
在您的代码中,您将提供
php://input
与往常一样。这种极端的分解不会得到任何结果,并且会导致非常脆弱的代码。您的测试应该表达对接口的期望,而不是您提供给它们的数据:在未来的版本中,PHP真的不能自由返回[“test2”=>“helloWorld”,“test1”=>“foobar”]
?如果你的代码被破坏了,它会被破坏吗?你认为你到底在测试什么
我觉得你把事情复杂化了
$a->doit
应该将$input
作为参数,而不是调用Koru::build
作为其初始化的一部分。然后您可以测试$a->doit
而不是测试
如果您坚持按此示例,则Koru::build
需要使用参数php://input“
–这通常被称为依赖项注入,您可以告诉函数需要知道的一切。然后,当你想“测试”东西时,你可以简单地传入一些其他文件(例如a)。使用双重测试
考虑到问题中的代码,最简单的解决方案是重新构造代码:
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');
}
尽可能避免使用静态类
问题中场景的大多数困难都是由使用静态类方法造成的,静态类使测试变得困难。如果可能,请避免使用静态类,并使用实例方法来解决相同类型的问题。通过使用,您可以直接使用以下方式对文件获取内容
函数进行猴补丁:
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'
]);
});
});
使用Zend\Diactoros\Stream
更多信息@CBroe-这需要为单元测试编写大量的框架。我认为这里的要点是,您不能对依赖于
php://input
直接执行。@CBroe我想编辑或创建php://input
直接在我的单元测试中,并将其传递给我想要的函数为了测试而不是提出真正的请求,我更容易管理单元测试。php://input
是全局状态,即。我建议您将文件的源代码传递给build()
函数,以明确其依赖关系。我正在创建一个类,帮助我将数组转换为对象,例如,如果我有$\u POST
,我可以使用Koru
来构建它,就像$data=Koru::build($\u POST)
,因此,我可以像$data->username
一样使用它,而不是$\u POST['username']
,我甚至可以添加一些帮助函数,这使我更方便地处理数据代码>更多的代码(只需要编写更多的代码,调试更多的代码,阅读更多的代码)并不是让世界变得更好,而是用更少的时间做更多的事情。停止编写“helper函数”,直到你们都确定没有其他人为你们编写它(堆栈溢出可以提供帮助),并且你们不止一次需要它。PHP真的不能在将来的版本中自由返回[“test2”=>“helloWorld”,“test1”=>“foobar”]?
这个答案的前提似乎有缺陷,这是永远不会发生的,但是如果是这样的话,代码就像你建议的那样,测试会通过,但代码仍然会失败。不。您编写测试不是为了练习代码行,而是为了验证接口,以便在更改代码时可以保留接口。此示例没有说明如何定义从vfs://input
,并在文件获取内容时出错('vfs://input“)
被调用。要定义内容,必须定义vfstream文件。在调用vfsStream:setup()
之前,也不需要调用vfsStreamWrapper::register()
。