Как сделать тест PHPUnit, который зависит от ~ реальных ~ данных POST/GET?

Я создал класс PHP, который включает в себя функции filter_input, чтобы упростить жизнь разработчика.
Чтобы проверить HTML-форму с полями url, name и age, код будет таким:

$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.

Проблема в том, что я понятия не имею, как подделать данные GET/POST в методе PHPUnit, не для этого случая.
Мне не нужно вставлять значения в $_POST, мне нужны "настоящие" данные, потому что filter_input работает с данными, полученными сценарием, а не с фактическим $_POST superglobal.

Я пытался использовать следующий тест 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
(извините за португальский, я знаю, что было бы лучше кодировать на английском языке, но здесь, где я работаю, все работает именно так. Если вы действительно считаете, что это действительно необходимо, я могу перевести код).

Любая идея о том, что я могу сделать, чтобы проверить этот класс?


person igorsantos07    schedule 11.11.2010    source источник
comment
Можете ли вы показать свой класс фильтра?   -  person Anti Veeranna    schedule 11.11.2010
comment
Идея состоит в том, чтобы написать свой код таким образом, чтобы вам не приходилось создавать настоящие данные POST — в этом нет смысла, расширение фильтра работает и тестируется по-разному, вам нужно протестировать ваш код . И чтобы дать вам предложения, как это сделать, вам нужно будет показать свой код.   -  person Anti Veeranna    schedule 11.11.2010
comment
Я не хочу портить ваш код, но у вас, вероятно, будет гораздо меньше головной боли, если вы воспользуетесь установленным классом проверки, таким как Zend_Validate.   -  person ryeguy    schedule 12.11.2010


Ответы (3)


Вы не можете подделать необработанные данные 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.

person netcoder    schedule 11.11.2010
comment
взгляните на код, который я добавил с последним редактированием, возможно, это поможет вам понять, что мне нужно... - person igorsantos07; 11.11.2010
comment
ну, я думаю, что это хорошая идея в любом случае. Я могу создать новый класс, например FilterT, который расширяет исходный класс и заставляет его использовать filter_var вместо filter_input. Работаем над этой идеей прямо сейчас. - person igorsantos07; 11.11.2010

Есть 2 проблемы с вашим кодом. Во-первых, вы обращаетесь к глобальным переменным, которые трудно протестировать. Во-вторых, вы жестко привязываете класс к конкретным данным (post, get и т. д.).

Что вам нужно сделать, так это заставить класс удовлетворять такой интерфейс:

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

Преимущества должны быть очевидны. Вам не нужно использовать $_POST или $_GET или любой другой предопределенный тип в качестве входных данных. Теперь вы можете не только проверять входные данные из любого источника (поскольку вы просто передаете их в конструктор), но, что более важно, вы можете вводить туда любые данные, которые вам нравятся, с целью тестирования.

Упс, я пропустил часть об использовании filter_input. Решение состоит в том, чтобы вместо этого использовать filter_var. Это позволяет запускать фильтры для любой переменной.

person ryeguy    schedule 11.11.2010
comment
Я почти нашел это решение, райгай. Даю вам один голос, потому что другой ответ привел меня к этому. знак равно - person igorsantos07; 12.11.2010

Один из подходов к этому состоит в том, чтобы использовать вспомогательный метод для запуска вашего 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() );
    }
}
person Mat Lipe    schedule 02.11.2019