如何进行依赖〜实际~POST / GET数据的PHPUnit测试?

时间:2010-11-11 19:12:52

标签: php testing phpunit

我创建了一个包含filter_input函数的PHP类,使我们的开发人员的生活更轻松 要使用urlnameage字段验证HTML表单,代码将是这样的:

$post = Filter::POST();
if ($post->validate_string('name') && $post->validate_integer('age')) {
    $url = $post->sanitize_url('url');
}

它将与:

相同
if (filter_input(INPUT_POST,'name',FILTER_UNSAFE_RAW) && filter_input(INPUT_POST,'age',FILTER_VALIDATE_INTEGER)) {
    $url = filter_input(INPUT_POST,'url',FILTER_SANITIZE_URL);
}

好吧,我认为代码已经完成,现在我想为它创建一个PHPUnit测试。

问题在于我不知道如何伪造PHPUnit方法上的GET / POST数据,而不是这种情况。
我不需要在$_POST中插入值,我需要“真实”数据,因为filter_input适用于脚本收到的数据,而不是实际的$_POST超全局数据。< / p>

我尝试使用以下PHPT测试和PHPUnit方法来实现这一点,但根本没有成功:

--TEST--
Generates POST and GET data to be used in FilterTest.php
--POST--
name=Igor&age=20
--GET--
name=Igor&age=19
--FILE--
<?php
echo $_POST['nome'].' = '.$_POST['idade'];
?>
--EXPECT--
Igor = 20

public function testPhpt() {
 $phpt = new PHPUnit_Extensions_PhptTestCase('FilterTest_data.phpt', array('cgi' => 'php-cgi'));
 $result = $phpt->run();
 $this->assertTrue($result->wasSuccessful());
}

修改

原始代码http://pastebin.com/fpw2fpxM
用于初步测试的代码http://pastebin.com/vzxsBQWm
(对不起葡萄牙语,我知道用英语编写代码会更好,但这就是我工作的地方。如果你真的认为它真的需要,我可以翻译代码。) / p>

我对如何测试这门课有什么想法?

3 个答案:

答案 0 :(得分:2)

您无法伪造原始POST数据。但问题出在你的代码中:它不是单元可测试的。而不是:

if (filter_input(INPUT_POST,'name',FILTER_UNSAFE_RAW) && filter_input(INPUT_POST,'age', FILTER_VALIDATE_INTEGER)) {
    $url = filter_input(INPUT_POST,'url',FILTER_SANITIZE_URL);
}

如果你有:

if (filter_var($data['name'], FILTER_UNSAFE_RAW) && filter_var($data['age'], FILTER_VALIDATE_INT)) {
    $url = filter_var($data['url'], FILTER_SANITIZE_URL);
}
// where $data is a copy of $_POST in that case

将您的代码单元呈现为可测试的,并且与您以前的代码相同的相同的内容

P.S。:FILTER_VALIDATE_INTEGER无效。适当的常量是FILTER_VALIDATE_INT

答案 1 :(得分:2)

您的代码有2个问题。一个是您正在访问难以测试的全局变量。第二个是你将类紧密绑定到特定数据(post,get等)。

你应该做的是让课程满足这种界面:

$filter = new Filter($_POST);
$filter->validate_string('name');

好处应该是显而易见的。您不必使用$_POST$_GET或任何其他预定义类型作为输入。您现在不仅可以验证来自任何源的输入(因为您只是将其传递给构造函数),更重要的是,您可以将任何数据注入到您喜欢的位置以进行测试。

Woops,我错过了关于使用filter_input的部分。解决方案是使用filter_var代替。它允许您对任何变量运行过滤器。

答案 2 :(得分:0)

一种解决方法是使用辅助方法在您的filter_input内部运行,然后在测试期间模拟此方法以使用其他类似filter_var的方法。

例如,此用例可以通过执行以下操作来完成:

class Data_Class {

    protected function i_use_filters() {
        if ( $this->filter_input( 'name', FILTER_UNSAFE_RAW ) && $this->filter_input( 'age', FILTER_VALIDATE_INT ) ) {
            $url = $this->filter_input( 'url', FILTER_SANITIZE_URL );
        }
        return $url;
    }

    protected function filter_input( $name, $filter ) {
        return filter_input( INPUT_POST, $name, $filter );
    }
}

class Test_Class extends TestCase {

    public function test_filters() : void {
        $mock = $this->getMockBuilder( Data_Class::class )
                        ->setMethods( [ 'filter_input' ] )
                        ->getMock();
        $mock->method( 'filter_input' )
                ->willReturnCallback( function ( $name, $filter ) {
                    if ( ! isset( $_POST[ $name ] ) ) {
                        return null;
                    }
                    return filter_var( $_POST[ $name ], $filter );
                } );

        $_POST[ 'name' ] = 'Myself';
        $_POST[ 'age'] = 40;
        $_POST[ 'url' ] = 'https://test.com';

        $this->assertEquals( 'https://test.com', $mock->i_use_filters() );
    }
}