自从我开始使用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: ...
而失败。
因此,我问你:有什么更好的办法吗?一种不涉及为我的fieldset中的每个字段写20多行的方法。如果它涉及重写我的控制器/字段集/视图模板(等),那就完全没问题了!
非常感谢任何帮助!此外,这是我第一次在一个论坛中用超过八年的编程问一些东西,所以如果有什么不清楚的话,请耐心等待。
此致 斯特芬
PS:我已经尝试过的是给IndexController
null而不是实际的表单,并在检测到表单为空时简单地中止视图模板。然而,虽然没有那么多的设置,但我基本上只是避免了视图模板的逻辑。因此,我无法检测视图模板中的错误。那不是我想要的。
答案 0 :(得分:0)
编辑IndexControllerTest:将private更改为其他位置的protected并将其扩展为新字段。每个新控制器必须覆盖调用parent :: methodname($ args)的方法并添加所需的代码......