【问题标题】:PHPUnit: Mocking Symfony2 FormBuilderInterface - type hinting issuesPHPUnit:模拟 Symfony2 FormBuilderInterface - 类型提示问题
【发布时间】:2012-12-19 03:34:54
【问题描述】:

我正在尝试测试扩展 Symfony\Component\Form\AbstractType 的类,但在使用 PHPUnit 模拟对象测试所需的 buildForm 方法时遇到问题:

class PageType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('name', 'text');
    }

    // ...
 }

这是一些示例测试代码:

public function testBuildForm()
{
    $form = new PageType($this->manager);
    $formBuilder = $this->getMock('Symfony\Component\Form\FormBuilderInterface');
    $form->buildForm($formBuilder, array());
}

很遗憾,对getMock() 的调用失败:

PHP Fatal error:  Class Mock_FormBuilderInterface_f36f83d4 must implement interface Traversable as part of either Iterator or IteratorAggregate in Unknown on line 0

我怀疑我可能遇到了这个错误: https://github.com/sebastianbergmann/phpunit/issues/604

所以,我创建了一个界面来解决这个问题:

use Symfony\Component\Form\FormBuilderInterface;

interface FormBuilderInterfaceExtender extends \Iterator, FormBuilderInterface
{
}

并更改了我的测试代码:

public function testBuildForm()
{
    $form = new PageType($this->manager);
    $formBuilder = $this->getMock('Pwn\ContentBundle\Tests\Helper\FormBuilderInterfaceExtender');
    $form->buildForm($formBuilder, array());
}

现在我有一个 FormBuilder 实例,但 PHP 不认为它是 Symfony\Component\Form\FormBuilderInterface 的实例,所以 $form->buildForm() 中的类型提示给出了这个错误:

Argument 1 passed to Pwn\ContentBundle\Form\Type\PageType::buildForm() must be an instance of Symfony\Component\Form\FormBuilderInterface, instance of Mock_FormBuilderInterfaceExtender_3527f313 given

在其他情况下,当我使用模拟对象时,类型提示和 instanceof 可以正常工作,但在这里却不行:

var_dump($formBuilder);
var_dump($formBuilder instanceof \Symfony\Component\Form\FormBuilderInterface);

给予

class Mock_FormBuilderInterfaceExtender_76df04af#19 (1) {
  private $__phpunit_invocationMocker =>
  NULL
}
bool(false)

看来我可以通过模拟一个实现接口的类来完成这项工作:

$formBuilder = $this->getMockBuilder('Symfony\Component\Form\FormBuilder')
                    ->disableOriginalConstructor()
                    ->getMock();

var_dump($formBuilder instanceof \Symfony\Component\Form\FormBuilderInterface);
// ^ gives bool(true)

但是,我应该能够使用界面本身吗?为什么扩展接口会“丢失”类型提示?

【问题讨论】:

  • 你试过assertInstanceOf而不是instanceof吗?在this thread 达到顶峰。
  • 刚试过,不过assertInstanceOf只是instanceof的一种方便的使用方式,所以效果是一样的。
  • 认为那里可能有一些魔法(你永远不会知道 w/ phpUnit)!我做了一个试金石(见下面的答案),看起来这应该有效,所以可能有点微妙。
  • 你试过getMock('Symfony\Component\Form\Tests\FormBuilderInterface')吗?

标签: php symfony phpunit


【解决方案1】:

似乎 PHPUnit 可以很好地模拟接口,并且扩展其他接口的接口没有问题。看看这个类和TestCase

命名空间A.php

<?php
namespace Testing;
interface NamespaceA {}

TestMockInterfaces.php

<?php
require 'NamespaceA.php';

interface Custom extends Testing\NamespaceA, Iterator, ArrayAccess {}

class TestMockInterfaces extends PHPUnit_Framework_TestCase
{
    public function testMocks()
    {
        $oSolidMock = $this->getMock('Iterator');
        $this->assertTrue($oSolidMock instanceof Iterator);

        $oSketchyMock = $this->getMock('Custom');
        $this->assertTrue($oSketchyMock instanceof Iterator);
        $this->assertTrue($oSketchyMock instanceof ArrayAccess);
        $this->assertTrue($oSketchyMock instanceof \Testing\NamespaceA);
    }
}

这通过了 100%,所以我怀疑还有其他事情发生。

在你模拟FormBuilderInterfaceExtender 之后,试试var_dump(class_implements($formBuilder));,看看它是否是你所期望的。

【讨论】:

  • 谢谢,非常有帮助......虽然阿德里安的回答解决了实际问题,但我之前没有遇到过class_implements;这让我们更容易看到发生了什么。
  • 好吧,你应该尝试模拟一个扩展 Traversable 的接口。
【解决方案2】:

当您无法测试 Symfony2 接口时,请查看组件的 Tests 目录,看看是否有专门用于测试的 mock。测试接口扩展了真实接口,并添加了使 phpunit 运行它们所需的任何调整。

在您的情况下,请尝试模拟 Symfony\Component\Form\Tests\FormBuilderInterface

【讨论】:

  • 谢谢,已经解决了,如果我再次遇到这种情况,我会留意那些测试类。
猜你喜欢
  • 2018-10-06
  • 2016-04-21
  • 2011-03-14
  • 2012-09-26
  • 2021-10-31
  • 1970-01-01
  • 1970-01-01
  • 2014-10-07
  • 1970-01-01
相关资源
最近更新 更多