Как тествате единица Zend_Form, която включва елемента CSRF форма?

Използвам CSRF скрития хеш елемент със Zend_Form и се опитвам да тествам единица за влизане, но не знам как да напиша Unit Test, за да включа този елемент. Погледнах в документите и прочетох колкото се може повече уроци, които можах да намеря. Дори вкусих всички, но никой не споменава това.


person joedevon    schedule 10.07.2009    source източник
comment
Актуализация: Малко е тъпо, но успях да получа формуляр, който може да се тества, защото зареждам своя Zend_Form от .ini файл. Разделих раздела csrf на раздел само за тестване, така че моите тестове позволяват влизане без преминаване през csrf. Надявам се това да помогне на някой друг. Между другото, мисля, че .ini файл е най-доброто място за зареждане на вашите формуляри.   -  person joedevon    schedule 11.07.2009
comment
Създателят на PHPUnit ми спомена, че End-to-End Testing != Unit Testing, което разбира се е вярно. В този случай не ме интересуваше да получа csrf, колкото исках да тествам дали мога да вляза и да видя някакъв елемент на получената страница. Невъзможността да се включи този елемент в теста убиваше влизането...   -  person joedevon    schedule 12.07.2009


Отговори (5)


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

За да тествате елемента, трябва да замените екземпляра Zend_Session_Namespace в елемента (със setSession) с такъв, който създавате сами и който съдържа правилния хеш (хешът се съхранява в ключ "hash")

За допълнителни примери вероятно бихте могли да разгледате модулните тестове на Zend Framework за класа Zend_Form_Element_Hash. Предполагам, че е трябвало да се справят и с това.

person Jani Hartikainen    schedule 12.07.2009
comment
Чувствам, че има нещо нередно, когато трябва да знаете толкова много за внедряването на CSRF, за да пишете тестове за контролери, които го използват. - person Eric Scrivner; 24.10.2011
comment
В по-контентен случай вероятно ще замените целия елемент на CSRF формуляр с подиграван екземпляр, тогава няма да е необходимо да се занимавате с нещата за съхранение на сесията - person Jani Hartikainen; 24.10.2011

Csrf стойността се генерира всеки път, когато формулярът се изобразява. Скритият елемент на формуляра се попълва предварително с тази стойност. Тази стойност също се съхранява в сесията. След изпращане на формуляра валидирането проверява дали стойността, публикувана от формуляра, се съхранява в сесията, ако не, тогава валидирането е неуспешно. От съществено значение е тази форма да бъде изобразена по време на теста (за да може да генерира скритата стойност и да я съхрани в сесията), след което можем да извлечем скритата стойност от изобразения 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');

    // ...
}
person Luke 10X    schedule 22.08.2010
comment
Това изглежда обещаващо, но $html = $this->getResponse()->getBody(); винаги е празно в моя случай. - person takeshin; 03.02.2011
comment
@takeshin не забравяйте да изпратите страницата с формуляра, преди да поискате тяло ;) - person Samuel Herzog; 02.03.2011
comment
#към днешна дата извърших модулни тестове, използвайки този подход и той работи. +1 към Лукас. - person jaym; 23.06.2011
comment
Ако класът на формуляра е наличен в тестове, можете действително да напишете $form = new MyForm(); $form->render(); вместо да изпращате страницата два пъти. Определено е по-бързо. - person fracz; 30.05.2014

Зададох променлива на средата в моя vhost файл на Apache, която казва на кода на кой сървър се изпълнява: разработка, етап или производство

Редът за vhost файла е:

SetEnv SITE_ENV "dev" 

Тогава просто карам формулярите си да реагират на подходящата среда:

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

Използвам същата техника за много неща. Например, ако IS dev, пренасочвам всички изходящи имейли към мен и т.н.

person lo_fye    schedule 12.07.2009
comment
Това е подобно на това, което в крайна сметка направих във файла forms.ini... Не знам защо, но не съм фен на IF изразите извън скелето или .ini файловете за специфично за околната среда боравене в основния код. Просто изглежда, че нова среда ще изисква търсене на много места, за да се поправи, а не .ini и bootstrap, където очаквате разлики.... - person joedevon; 13.07.2009

Отговорих на по-скорошен въпрос, подобен на този. Поставям отговора си тук, в случай че ще помогне на някого в бъдеще.

Наскоро намерих страхотен начин за тестване на формуляри с хеш елементи. Това ще използва фиктивен обект, за да премахне хеш елемента и няма да се налага да се притеснявате за това. Дори няма да се налага да правите session_start или нещо подобно по този начин. Няма да се налага и да „предварително изобразявате“ формуляра.

Първо създайте клас "stub" така

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

Зададеният метод наистина е там само за целите на тестването. Вероятно изобщо няма да го използвате по време на реална употреба, но сега в 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 ТРЯБВА да предадете хеш елемента по време на конструирането на формуляр. Ако не го направите, вашият init() метод ще бъде извикан, преди хешът ви да може да бъде зададен с метода set и в крайна сметка ще използвате обикновения хеш елемент. Ще знаете, че използвате обикновен хеш елемент, защото вероятно ще получите някаква грешка в сесията, ако НЕ сте свързани към база данни.

Кажете ми, ако намирате това за полезно или ако го използвате.

person Jerry Saravia    schedule 26.04.2012

Решението за ZF2 е създаване на вашия формуляр в тест и получаване на стойност от вашия 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);
person Alex Joe    schedule 06.01.2015