6

我正在使用带有 Zend_Form 的 CSRF 隐藏哈希元素并尝试对登录进行单元测试,但不知道如何编写单元测试来包含该元素。查看文档并阅读尽可能多的教程。我什至都把它们都吃了,但没有人提到这一点。

4

5 回答 5

9

每次呈现表单时都会生成 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');

    // ...
}
于 2010-08-22T13:14:00.810 回答
3

正确的散列存储在会话中,散列表单元素有一个 Zend_Session_Namespace 实例,其中包含散列的名称空间。

要对元素进行单元测试,您可以将元素中的 Zend_Session_Namespace 实例(使用 setSession)替换为您自己创建的包含正确散列的实例(散列存储在键“散列”中)

对于更多示例,您可能可以查看 Zend_Form_Element_Hash 类的 Zend Framework 单元测试。我想他们也不得不处理这个问题。

于 2009-07-12T19:56:20.577 回答
1

我在我的 Apache vhost 文件中设置了一个环境变量,它告诉代码它在哪个服务器上运行:开发、登台或生产

vhost 文件的行是:

SetEnv SITE_ENV "dev" 

然后我只是让我的表单对适当的环境做出反应:

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

我对很多东西都使用了同样的技术。例如,如果它是开发者,我会将所有外发电子邮件重定向给我,等等。

于 2009-07-12T19:36:46.827 回答
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 中,您必须在表单构建期间传递哈希元素。如果你不这样做,你的init()方法将在你的存根散列可以用 set 方法设置之前被调用,你最终将使用常规的散列元素。您会知道您使用的是常规哈希元素,因为如果您没有连接到数据库,您可能会收到一些会话错误。

让我知道您是否觉得这有帮助或者您是否使用它。

于 2012-04-26T05:18:06.853 回答
1

ZF2 的解决方案是在测试中创建您的表单,并从您的 csrf 表单元素中获取价值:

        $form = new  \User\Form\SignupForm('create-user');
        $data = [
            'security' => $form->get('security')->getValue(),
            'email' => 'test@test.com',
            'password' => '123456',
            'repeat-password' => '123456',
        ];
        $this->dispatch('/signup', 'POST', $data);
于 2015-01-06T09:29:31.590 回答