Вопрос по phpunit, csrf, zend-form, zend-framework – Как вы тестируете модуль Zend_Form, который включает элемент формы CSRF?

6

Я использую скрытый хеш-элемент CSRF с Zend_Form и пытаюсь выполнить модульное тестирование входа в систему, но не знаю, как написать модульный тест для включения этого элемента. Заглянул в документы и прочитал столько учебников, сколько смог найти. я дажеделикатес их всех, но никто не упоминает об этом.

Создатель PHPUnit упомянул мне, что «Сквозное тестирование! = Модульное тестирование», что, конечно, верно. В этом случае я не заботился о получении csrf столько, сколько хотел проверить, что я могу войти в систему и увидеть некоторый элемент на получающейся странице. Невозможность включить этот элемент в тест убивала логин ... joedevon
Обновление: Это немного глупо, но я смог получить тестируемую форму, потому что я загружаю свой Zend_Form из INI-файла. Я разделил секцию csrf на секцию только для тестирования, поэтому мои тесты разрешают вход в систему без прохождения через csrf. Надеюсь, это поможет кому-то еще. Кстати, я думаю, что файл .ini - лучшее место для загрузки ваших форм. joedevon

Ваш Ответ

5   ответов
1

чения из вашего элемента формы csrf:

        $form = new  \User\Form\SignupForm('create-user');
        $data = [
            'security' => $form->get('security')->getValue(),
            'email' => '[email protected]',
            'password' => '123456',
            'repeat-password' => '123456',
        ];
        $this->dispatch('/signup', 'POST', $data);
спасибо, вы сэкономили мне много времени
1

который сообщает коду, на каком сервере он работает: разработка, постановка или производство

Строка для файла vhost:

SetEnv SITE_ENV "dev" 

Тогда я просто заставляю свои формы реагировать на соответствующую среду:

if($_SERVER['SITE_ENV']!='dev')
{
   $form_element->addValidator($csrf_validator);
}

Я использую эту же технику для многих вещей. Например, если это dev, я перенаправляю всю исходящую почту мне и т. Д.

Это похоже на то, что я в итоге делал в файле forms.ini ... Я не знаю, почему, но не фанат операторов IF за пределами скаффолдинга или файлов .ini для специфической для среды обработки внутри основного кода. Похоже, что новая среда потребовала бы поиска в сотне мест, чтобы исправить это, а не .ini и начальной загрузки, где вы ожидаете различий .... joedevon
9

элемент формы заполняется этим значением. Это значение также сохраняется в сеансе. После отправки формы проверка проверяет, сохраняется ли значение, отправленное из формы, в сеансе, если нет, то проверка завершается неудачно. Важно, чтобы форма отображалась во время теста (чтобы она могла генерировать скрытое значение и сохранять его в сеансе), затем мы можем извлечь то, что является скрытым значением, из визуализированного html, а позже мы можем добавить скрытое значение хеша в наш запрос. Рассмотрим этот пример:

function testAddPageStoreValidData()
{
    // render the page with form 
    $this->dispatch('/form-page');

    // fetch content of the page 
    $html = $this->getResponse()->getBody();

    // parse page content, find the hash value prefilled to the hidden element
    $dom = new Zend_Dom_Query($html);
    $csrf = $dom->query('#csrf')->current()->getAttribute('value');

    // reset tester for one more request
    $this->resetRequest()
         ->resetResponse();

    // now include $csrf value parsed from form, to the next request
    $this->request->setMethod('POST')
                  ->setPost(array('title'=>'MyNewTitle',
                                  'body'=>'Body',
                                  'csrf'=>$csrf));
    $this->dispatch('/form-page');

    // ...
}
Если класс формы доступен в тестах, вы можете написать$form = new MyForm(); $form->render(); вместо отправки страницы дважды. Это определенно быстрее.
Это выглядит многообещающе, однако$html = $this->getResponse()->getBody(); всегда пусто в моем случае.
@takeshin Не забудьте отправить страницу с формой, прежде чем просить тело;)
# На сегодняшний день я выполнил модульные тесты, используя этот подход, и он работает. +1 к Лукасу.
3

а элемент формы Hash имеет экземпляр Zend_Session_Namespace, который содержит пространство имен для хеша.

Чтобы выполнить модульное тестирование элемента, вы должны заменить экземпляр Zend_Session_Namespace в элементе (с помощью setSession) на тот, который вы создаете сами, который содержит правильный хеш (хеш хранится в ключе & quot; hash & quot;)

Для дальнейших примеров вы можете посмотреть на модульные тесты Zend Framework для класса Zend_Form_Element_Hash. Я бы предположил, что им пришлось иметь дело с этим.

В более ограниченном случае вы, вероятно, заменили бы весь элемент формы CSRF на макетированный экземпляр, тогда вам не нужно разбираться с вещами хранения сессии
Я чувствую, что что-то не так, когда нужно много знать о реализации CSRF для написания тестов для контроллеров, которые его используют.
1

похожий на этот. Я также размещаю здесь свой ответ на случай, если он кому-нибудь поможет в будущем.

Недавно я нашел отличный способ тестирования форм с хэш-элементами. Это будет использовать фиктивный объект, чтобы заглушить хеш-элемент, и вам не придется об этом беспокоиться. Вы даже не должны будете делать session_start или что-то еще таким образом. Вы не должны «предсказать»; форма тоже.

Сначала создайте «заглушку» класс как так

class My_Form_Element_HashStub extends Zend_Form_Element_Hash
{
    public function __construct(){}
}

Затем добавьте в форму следующее.

class MyForm extends Zend_Form
{

    protected $_hashElement;

    public function setHashElement( Zend_Form_Hash_Element $hash )
    { 
        $this->_hashElement = $hash; 
        return $this; 
    }

    protected function _getHashElement( $name = 'hashElement' )
    { 
        if( !isset( $this->_hashElement )
        {
            if( isset( $name ) )
            {
                $element = new Zend_Form_Element_Hash( $name, 
                                                  array( 'id' => $name ) );
            }
            else
            {
                $element = new Zend_Form_Element_Hash( 'hashElement', 
                                        array( 'id' => 'hashElement' ) );
            }

            $this->setHashElement( $element );
            return $this->_hashElement;
        }
    }

    /**
     * In your init method you can now add the hash element like below
     */
    public function init()
    {
        //other code
        $this->addElement( $this->_getHashElement( 'myotherhashelementname' );
        //other code
    }
}

Метод set существует только для тестирования. Вы, вероятно, вообще не будете его использовать при реальном использовании, но теперь в phpunit вы можете исправить следующее.

class My_Form_LoginTest extends PHPUnit_Framework_TestCase
{

    /**
     *
     * @var My_Form_Login
     */
    protected $_form;
    /**
     *
     * @var PHPUnit_Framework_MockObject_MockObject
     */
    protected $_hash;

    public function setUp()
    {
        parent::setUp();
        $this->_hash = $this->getMock( 'My_Form_Element_HashStub' );

        $this->_form = new My_Form_Login( array(
                    'action'                    => '/',
                    'hashElement'               => $this->_hash
    }

    public function testTrue()
    {   
        //The hash element will now always validate to true
        $this->_hash
             ->expects( $this->any() )
             ->method( 'isValid' )
             ->will( $this->returnValue( true ) );

        //OR if you need it to validate to false
        $this->_hash
             ->expects( $this->any() )
             ->method( 'isValid' )
             ->will( $this->returnValue( true ) );
    }
}

Вы должны создать свой собственный заглушку. Вы не можете просто вызвать метод phpunit.getMockObject() потому что это будет непосредственно расширять хеш-элемент, а обычный хеш-элемент делает «зло» вещи в своем конструкторе.

С помощью этого метода вам даже не нужно подключаться к базе данных для проверки ваших форм! Мне понадобилось время, чтобы подумать об этом.

Если вы хотите, вы можете нажатьsetHashElement() метод (вместе с переменной и методом get) в некоторый базовый класс FormAbstract.

ПОМНИТЕ, в phpunit вы должны передать элемент hash во время создания формы. Если вы этого не сделаете, вашinit() Метод будет вызван до того, как ваш хэш заглушки может быть установлен с помощью метода set, и вы в конечном итоге будете использовать обычный элемент хеша. Вы будете знать, что используете обычный хэш-элемент, потому что вы, вероятно, получите ошибку сеанса, если вы НЕ подключены к базе данных.

Дайте мне знать, если вы найдете это полезным или если вы используете его.

Похожие вопросы