【问题标题】:Symfony2 Unit testing form submissionSymfony2 单元测试表单提交
【发布时间】:2014-06-28 02:55:01
【问题描述】:

我有一个看起来像这样的表单,当它被渲染时:

<form name="service_user_registration" method="post" action="" novalidate="novalidate">

        <div>
             <label for="service_user_registration_user_email" class="required">E-mail</label>
             <input type="email" id="service_user_registration_user_email" name="service_user_registration[user][email]" required="required" size="30" class="sample_class" title="Sample title" placeholder="Your e-mail..." />
        </div>

        <div>
             <label for="service_user_registration_account_name" class="required">Company name</label>
             <input type="text" id="service_user_registration_account_name" name="service_user_registration[account][name]" required="required" placeholder="Your company name..." />
        </div>

        <button type="submit" id="service_user_registration_register" name="service_user_registration[register]" class="btn btn-primary">Register</button>

    <input type="hidden" id="service_user_registration__token" name="service_user_registration[_token]" value="VzkWJQw-2p-1lfBJoT5xwNB4dR2reT47i0ZTZES9LqY" />
</form>

我想对表单提交进行单元测试,如果我输入的数据写在底层对象中,这是一个注册对象。我像下面那样做,但我不知道如何正确设置 $data 数组,因为字段的 name 属性看起来像一个数组。

class RegistrationTypeTest extends TypeTestCase
{
    public function testSubmit()
    {
        $user = new User();
        $account = new Account();
        $registration = new Registration($user,$account);

        $form = $this->factory->create(new RegistrationType(), $registration);

        $data = array(
            'service_user_registration[user][email]' => 'testing@test.com',
            'service_user_registration[account][name]' => 'New company'
        );

        $form->submit($data);

        $this->assertTrue($form->isSynchronized());

        $formData = $form->getData();

        $this->assertInstanceOf('Service\Bundle\UserBundle\Entity\Registration', $formData);

        $this->assertEquals($data['service_user_registration[user][email]'],   $formData->getUser()->getEmail());
        $this->assertEquals($data['service_user_registration[account][name]'], $formData->getAccount()->getName());
    }
}

最后两个断言失败,因为 Registration 对象中的 $formData 全部为空,当提交的字段都为空时应该是这种情况。我也把 $data 写成一个数组,但没有改变任何东西。

编辑(添加表单类型):

class RegistrationType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('user', new RegistrationUserType())
                ->add('account', new RegistrationAccountType())
                ->add('register', 'submit');
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        # Using cascade validation because Valid constraint can not have validation groups

        $resolver->setDefaults([
            'cascade_validation' => true,
            'intention' => $this->getName()
        ]);
    }

    public function getName()
    {
        return 'service_user_registration';
    }
}

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('registration'),
            'data_class' => 'Service\Bundle\UserBundle\Entity\User',
            'intention' => $this->getName()
        ));
    }

    public function getName()
    {
        return 'service_user_user_registration';
    }
}

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

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => array('registration'),
            'data_class' => 'Service\Bundle\UserBundle\Entity\Account',
            'intention' => $this->getName()
        ));
    }

    public function getName()
    {
        return 'service_user_account_registration';
    }
}

【问题讨论】:

  • 我们可以看看您的表单类型吗?
  • 当然,我已经添加了表单类型。手动测试时,所有代码都能完美运行。我只有单元测试有问题。感谢您的努力。
  • 您没有在单元测试中与数据一起提交 csrf-token。所以我猜$form-&gt;isValid() 在你的单元测试中返回 false ?你的主要问题是$formData-&gt;getUser()-&gt;getEmail()$formData-&gt;getAccount()-&gt;getName() 都返回null,对吧?
  • $this->assertTrue($form->isValid()); // 这没关系,它不会输出错误。是的,最后两个断言都返回 null。
  • 我表达错了。两个断言都会这样说:断言 null 匹配预期的 'testing@test.com' 失败。

标签: php forms unit-testing symfony


【解决方案1】:

然后人们继续抱怨 TDD 很愚蠢,很难,很烂,最后,it's dead...

帮自己一个忙,不要再尝试对本来就不应该进行单元测试的东西进行单元测试。写一些更高级别的测试。您至少有两个选择。

首先,您可以编写特定于该表单的集成测试,以在更广泛的上下文中测试其行为。例如,您可以创建表单的一个实例,将数据传递给它,然后测试您从表单中获取的模型是否具有您传递的数据。

其次,您可以编写涉及此表单的端到端测试。如果表单不能正常工作,测试将失败。

由于端到端测试通常较慢,您实际上可以编写其中的一两个来测试您的代码在表单有效和无效时的行为是否正确。例如,当表单有效时,模型会被持久化,并且用户会被重定向到某个地方。如果不是,则不会保留任何内容,并且表单会再次显示先前输入的数据和验证消息。

然后您可以编写几个特定于表单的集成测试来测试其他一些数据输入组合,以确保它在这些情况下表现正确。

实际上,如果您正在测试验证,您可以省略表单本身的集成测试,而直接在模型上编写测试来测试验证规则。但是您仍然需要端到端测试来测试应用的整体流程。

【讨论】:

  • 感谢您的回答!正如您在“第一个”选项中所说,我实际上正在尝试针对此表单进行集成测试。尝试将数据传递给表单并查看它是否进入模型。到目前为止,编写功能测试是可行的,这可能是您在第二个选项中提出的,对吧?
  • 嗯,是的,它很接近。但是TypeTestCase 测试仍然是低级别的,因为它们与容器隔离。例如,他们没有加载任何表单扩展。所以,我建议在容器就位的情况下运行表单集成测试。忘记TypeTestCase——它是用来单独测试可重用表单类型的。