自从我开始使用Zend Framework 3以来,我在测试控制器时遇到了问题。我试图用PhpUnit 5.7测试我的控制器,我的控制器依赖于Zend Form,它与Doctrine的DoctrineObject进行了合并。
我试图把这个尽可能的简单,所以这里是一个的给我设置的一个小例子,头痛:为表格控制器编写测试
控制器:
class IndexController extends AbstractActionController {
private $form;
public function __construct(AlbumForm $form) {
$this->form = $form;
}
public function indexAction() {
return ['form' => $this-form];
}
}
的ControllerFactory:
class IndexControllerFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, ...) {
$formManager = $container->get('FormElementManager');
return new IndexController($formManager->get(AlbumForm::class));
}
}
的在albums/index/index.phtml
对应的视图模板:
<?php
$this->form->prepare();
$this->form->setAttribute('action', $this->url(null, [], true));
$albumFieldset = $this->form->get('album');
?>
<?= $this->form()->openTag($this-form) ?>
<div class="form-group">
<?= $this->formRow($albumFieldset->get('name')) ?>
</div>
<?= $this->form()->closeTag() ?>
形式:
class AlbumForm extends Form {
public function init() {
$this->add([
'name' => 'albumFieldset',
'type' => AlbumFieldset::class,
'options' => [
'use_as_base_fieldset' => true,
],
]);
}
}
的字段集:
class AlbumFieldset extends Fieldset {
public function init() {
$this->add([
'name' => 'name',
'type' => Text::class,
'options' => [
'label' => 'Name of album',
],
]);
}
}
的FieldsetFactory:
class AlbumFieldsetFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, ...) {
$objectManager = $container->get(ObjectManager::class);
$fieldset = new AlbumFieldset();
$fieldset->setHydrator(new DoctrineObject($objectManager));
$fieldset->setObject(new Album());
return $fieldset;
}
}
现在,到目前为止一切是伟大的工作。
但是,当为此编写测试时,我遇到了麻烦。让我先告诉你我到目前为止:
class IndexControllerTest extends AbstractHttpControllerTestCase {
protected function setUp() {
parent::setUp();
$this->configureServiceManager($this->getApplicationServiceLocator());
}
private function configureServiceManager(ServiceManager $services) {
$services->setAllowOverride(true);
$services->setService(ObjectManager::class, $this->mockObjectManager()->reveal());
$services->setService('FormElementManager', $this->mockFormManager()->reveal());
$services->setAllowOverride(false);
}
private $objectManager;
private function mockObjectManager() {
$this->objectManager = $this->prophesize(ObjectManager::class);
return $this->objectManager;
}
private $formManager;
private function mockFormManager() {
$this->formManager = $this->prophesize(FormElementManager::class);
$this->formManager->get(AlbumForm::class)->willReturn($this->mockForm()->reveal());
return $this->formManager;
}
private $form;
private function mockForm() {
$this->form = $this->prophesize(AlbumForm::class);
$this->form->prepare()->willReturn(null);
$this->form->setAttribute('action', Argument::type('string'))->willReturn(null);
$this->form->getAttributes()->willReturn([]);
$this->form->get('album')->willReturn($this->mockAlbumFieldset()->reveal());
return $this->form;
}
private $albumFieldset;
private function mockAlbumFieldset() {
$this->albumFieldset = $this->prophesize(AlbumFieldset::class);
$this->albumFieldset->get('name')->willReturn($this->mockName()->reveal());
return $this->albumFieldset;
}
private $name;
private function mockName() {
$this->name = $this->prophesize(Text::class);
$this->name->getLabel()->willReturn('label');
$this->name->getLabelAttributes()->willReturn(['for' => 'name']);
$this->name->getLabelOption('disable_html_escape')->willReturn(false);
$this->name->getLabelOption('always_wrap')->willReturn(false);
$this->name->getLabelOption('label_position')->willReturn('prepend');
$this->name->getName('album[name]');
$this->name->getAttribute('type')->willReturn('text');
$this->name->hasAttribute('id')->willReturn(true);
$this->name->getAttribute('id')->willReturn('name');
$this->name->getAttributes([])->willReturn([]);
$this->name->getValue()->willReturn(null);
$this->name->getMessages()->willReturn([]);
return $this->name;
}
}
这将最终运行没有错误。不过,我想提请你注意最后几种方法,特别是mockName()
。大多数这些定义是完全默认的,并且几乎没有一个在开头(仅名称是)在AlbumFieldset中指定。这是非常讨厌的写下他们的每表格输入我可能有写下这实际上引入更多的错误比它解决。例如,我仍然不确定always_wrap
的正确标签选项是什么。实际上我根本不在乎这个选择,但是我必须在测试中写下一些关于它的内容,否则测试会失败,并显示'Prophecy\Exception\Call\UnexpectedCallException' with message 'Method call: - getLabelOption("always_wrap") on Double\Zend\Form\Element\Text\P245 was not expected, expected calls were: ...
。
因此,我问你:有没有更好的方法来解决这个问题?一种不涉及为我的字段集中的每个字段写入20行以上的行的方法。如果它涉及到重写我的控制器/字段集/视图模板(等),那就完全没问题!
任何帮助,非常感谢!另外,这是我第一次在论坛上用八年多的时间来编写一些东西,所以如果有什么不清楚的话,请耐心等待。
此致 斯特芬
PS:我已经尝试是给IndexController
空的,而不是实际的形式,简单地中止视图模板,当它检测到窗体为空。然而,尽管没有那么多的设置,我基本上只是避免视图模板的逻辑。因此,我无法检测视图模板中的错误。这不是我想要的。