Zend framework 如何对包含CSRF表单元素的Zend_表单进行单元测试?
我正在使用带有Zend_表单的CSRF隐藏哈希元素,并尝试对登录进行单元测试,但不知道如何编写单元测试来包含该元素。我查阅了文档,阅读了尽可能多的教程。我甚至不知道,但没有人提到这一点。我在Apache vhost文件中设置了一个环境变量,它告诉代码它在哪个服务器上运行: 开发、分段或生产 vhost文件的行是:Zend framework 如何对包含CSRF表单元素的Zend_表单进行单元测试?,zend-framework,zend-form,phpunit,csrf,Zend Framework,Zend Form,Phpunit,Csrf,我正在使用带有Zend_表单的CSRF隐藏哈希元素,并尝试对登录进行单元测试,但不知道如何编写单元测试来包含该元素。我查阅了文档,阅读了尽可能多的教程。我甚至不知道,但没有人提到这一点。我在Apache vhost文件中设置了一个环境变量,它告诉代码它在哪个服务器上运行: 开发、分段或生产 vhost文件的行是: SetEnv SITE_ENV "dev" 然后,我只是让表单对适当的环境做出反应: if($_SERVER['SITE_ENV']!='dev') { $form_elem
SetEnv SITE_ENV "dev"
然后,我只是让表单对适当的环境做出反应:
if($_SERVER['SITE_ENV']!='dev')
{
$form_element->addValidator($csrf_validator);
}
我在很多事情上都使用同样的技术。例如,如果是dev,我会将所有传出的电子邮件重定向给我,等等。正确的哈希存储在会话中,并且hash form元素有一个Zend_session_名称空间实例,其中包含哈希的名称空间 要对元素进行单元测试,您需要将元素中的Zend_Session_名称空间实例(使用setSession)替换为您自己创建的包含正确哈希的实例(哈希存储在“hash”键中)
有关更多示例,您可能会查看Zend_Form_Element_哈希类的Zend框架单元测试。我认为他们也必须处理这个问题。每次呈现表单时都会生成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');
// ...
}
我最近回答了一个类似的问题。我把我的答案也放在这里,以防将来对任何人都有帮助 我最近发现了一种使用散列元素测试表单的好方法。这将使用一个模拟对象来存根散列元素,您不必担心它。你甚至不需要这样做一个会话启动或任何事情。您也不必“预渲染”表单 首先像这样创建一个“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
}
}
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中,在表单构造期间必须传递哈希元素。如果不这样做,则在使用set方法设置存根哈希之前,将调用init()
方法,并最终使用常规哈希元素。您将知道您正在使用常规哈希元素,因为如果未连接到数据库,您可能会遇到一些会话错误
请告诉我您是否觉得这有帮助或是否使用了它。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);
更新:这有点麻烦,但是我能够得到一个可测试的表单,因为我正在从一个.ini文件加载我的Zend_表单。我将csrf部分拆分为仅测试部分,这样我的测试允许登录而不通过csrf。希望这对其他人有帮助。顺便说一句,我认为.ini文件是加载表单的最佳位置。PHPUnit的创建者向我提到“端到端测试!=单元测试”,这当然是正确的。在这种情况下,我并不关心如何使用csrf,因为我想测试我是否可以登录并在结果页面上看到一些元素。无法在测试中包含此元素会终止登录…这与我在forms.ini文件中所做的类似。。。我不知道为什么,但不喜欢脚手架之外的IF语句,也不喜欢在主代码内部处理特定于环境的.ini文件。似乎一个新的环境需要寻找大量的地方来修复它,而不是.ini和bootstrap,在这些地方你会发现差异……这看起来很有希望,但是
$html=$This->getResponse()->getBody()
在我的情况下总是空的。@takeshin在请求正文之前,不要忘了发送带有表单的页面;)#到目前为止,我使用这种方法进行了单元测试,并且效果良好+如果form类在测试中可用,那么实际上可以编写$form=newmyform()$表单->渲染()代码>而不是两次调度页面。它肯定更快。我觉得如果你必须了解CSRF的实现,才能为使用它的控制器编写测试,那就错了。在一个更包含的情况下,你可能会用一个模拟的实例替换整个CSRF表单元素,那么你就不需要处理会话存储了