Symfony 3 测试 - "AlreadySubmittedException" 表单测试

Symfony 3 Testing - "AlreadySubmittedException" on form testing

我在 SF3 项目中测试创建表单(对于简单的 Bank 实体)时遇到了一些问题。这里是测试代码:

class BankControllerTest extends WebTestCase
{
    /**
     * Test the creation form action
     */
    function testNewAction()
    {
        $client = static::createClient();

        // User must be logged in
        $client->request('GET', '/bank/bank/new');
        $response = $client->getResponse();
        $this->assertEquals(302, $response->getStatusCode());

        // Page request test
        $client = LoginControllerTest::createClientConnectedTest();
        $crawler = $client->request('GET', '/bank/bank/new');
        $response = $client->getResponse();
        $this->assertEquals(200, $response->getStatusCode());

        // Form testing
        $formData = array(
            'bank[name]'    => 'BankTest '.uniqid(),
            'bank[comment]' => 'Tast bank '.uniqid().' comment.',
        );
        $submitButtonCrawlerNode = $crawler->selectButton('Create');
        $form = $submitButtonCrawlerNode->form();
        $client->submit($form, $formData);
        $response = $client->getResponse();
        $container = $client->getContainer();
        $this->assertEquals(301, $response->getStatusCode());
    }
}

我感兴趣的部分是最后"Form testing"部分,我把所有内容都粘贴了以防我之前弄错了。

我想要的不是“301”httpCode,而是 500 错误:

vendor/bin/phpunit src/BillBrother/BankAccountBundle
[...]
1) BillBrother\BankAccountBundle\Test\Controller\BankControllerTest::testNewAction
Failed asserting that 500 matches expected 301.

    /home/developer/src/BillBrother/BankAccountBundle/Test/Controller/BankControllerTest.php:47

并且看到它是一个 "AlreadySubmittedException",它产生了这个错误:

[2016-06-30 08:00:18] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Form\Exception\AlreadySubmittedException: "You cannot add children to a submitted form" at /home/developer/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php line 802 {"exception":"[object] (Symfony\Component\Form\Exception\AlreadySubmittedException(code: 0): You cannot add children to a submitted form at /home/developer/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php:802)"} []

我是不是做错了什么?

在此先感谢您的帮助。


这里是我在这个测试中使用的一些其他代码部分:

登录测试文件,我在其中使用 createClientConnectedTest() 静态函数:

/**
 * BillBrother\AuthBundle\Controller test class file.
 *
 * @TODO Write tests
 * @author Neimheadh <contact@neimheadh.fr>
 */
namespace BillBrother\AuthBundle\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;

/**
 * BillBrother\AuthBundle\Controller test class.
 */
class LoginControllerTest extends WebTestCase
{
    /**
     * Create a connected client.
     *
     * @param string $login
     * @param string $password
     * @return \Symfony\Bundle\FrameworkBundle\Client
     */
    public static function createClientConnected($login, $password)
    {
        return static::createClient(array(), array(
            'PHP_AUTH_USER'     => $login,
            'PHP_AUTH_PW'       => $password
        ));
    }

    /**
     * Create a client connected with test account
     *
     * @return \Symfony\Bundle\FrameworkBundle\Client
     */
    public static function createClientConnectedTest()
    {
        return static::createClientConnected(
            'test@email.com',
            'password'
        );
    }

    /**
     * Test the login form
     */
    public function testLoginform()
    {
        $client = static::createClient();

        $crawler = $client->request('GET', '/login');
    }

}

BankType class :

/**
 * Bank entity type
 *
 * @author Neimheadh <contact@neimheadh.fr>
 */
namespace BillBrother\BankAccountBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

use Symfony\Component\Form\Extension\Core\Type\TextareaType;

/**
 * Bank type form
 */
class BankType extends AbstractType
{
    /**
     * Build the form
     *
     * @param FormBuilderInterface $builder
     * @param array $options
     */
     public function buildForm(FormBuilderInterface $builder, array $options)
     {
         $builder
            ->add('name')
            ->add('comment', TextareaType::class)
        ;
     }

     /**
      * Configure the form default options.
      *
      * @param OptionsResolver $resolver
      */
     public function configureOptions(OptionsResolver $resolver)
     {
         $resolver->setDefaults(array(
             'data_class' => 'BillBrother\BankAccountBundle\Entity\Bank'
         ));
     }
}

Bank实体class:

/**
 * BillBrother bank entity class file.
 *
 * @author Neimheadh <contact@neimheadh.fr>
 */
namespace BillBrother\BankAccountBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use BillBrother\AuthBundle\Entity\User;
use Datetime;

/**
 * Bank
 *
 * @ORM\Table(name="bank")
 * @ORM\Entity(repositoryClass="BillBrother\BankAccountBundle\Repository\BankRepository")
 */
class Bank
{
    /**
     * The bank id.
     *
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * Bank creator
     *
     * The user account who created the bank.
     *
     * If null, it means the bank was created by the system.
     *
     * @var User
     *
     * @ORM\JoinColumn(name="creator_id", nullable=true)
     * @ORM\ManyToOne(targetEntity="BillBrother\AuthBundle\Entity\User")
     */
    private $creator;

    /**
     * The bank name.
     *
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * Comments about the bank.
     *
     * @var string
     *
     * @ORM\Column(name="comment", type="text")
     */
    private $comment;

    /**
     * Bank row creation date
     *
     * @var Datetime
     *
     * @ORM\Column(name="create_date", type="datetime")
     */
    private $createDate;

    /**
     * Constuctor
     *
     * Initialize the creation date to the current date
     */
    public function __construct()
    {
        $this->createDate = new Datetime;
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name, nullable=tru
     * @return Bank
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set comment
     *
     * @param string $comment
     *
     * @return Bank
     */
    public function setComment($comment)
    {
        $this->comment = $comment;

        return $this;
    }

    /**
     * Get comment
     *
     * @return string
     */
    public function getComment()
    {
        return $this->comment;
    }

    /**
     * Set creator
     *
     * @param User $creator
     *
     * @return Bank
     */
    public function setCreator(User $creator = null)
    {
        $this->creator = $creator;

        return $this;
    }

    /**
     * Get creator
     *
     * @return User
     */
    public function getCreator()
    {
        return $this->creator;
    }
}

控制器调用:

/**
 * BillBrother bank account bundle bank controller class file.
 *
 * @author Neimheadh <contact@neimheadh.fr>
 */
namespace BillBrother\BankAccountBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;

use BillBrother\BankAccountBundle\Entity\Bank;
use BillBrother\BankAccountBundle\Form\Type\BankType;

/**
 * BillBrother application bank account bank controller.
 */
class BankController extends Controller
{
    /**
     */
    private function _buildForm(Request $request, Bank $bank)
    {
        $form = $this->createForm(BankType::class, $bank);
        $form->handleRequest($request);

        if($form->isValid()) {
            $em = $this->getDoctrine()->getEntityManager();
            $em->persist($bank);
            $em->flush();
        }

        $form->add('submit', SubmitType::class, array(
            'label' => 'Create'
        ));
        return $form;
    }

    /**
     * Bank account bundle bank list page action.
     *
     * @Route("/banks", name="billbrother_bankaccount_bank_list")
     */
    public function listAction()
    {
        return $this->render('BillBrotherBankAccountBundle:Bank:list.html.twig');
    }

    /**
     * Create a new bank page action.
     *
     * @param Request $request
     *
     * @Route("/bank/new", name="billbrother_bankaccount_bank_new")
     */
    public function newAction(Request $request)
    {
        $bank = new Bank();
        $form = $this->_buildForm($request, $bank);

        if($form->isValid())
            return $this->redirectToRoute(
                'billbrother_bankaccount_bank_edit',
                array('id'=>$bank->getId())
            );

        return $this->render(
            'BillBrotherBankAccountBundle:Bank:form.html.twig',
            array(
                'form'  => $form->createView()
            )
        );
    }

    /**
     * Modify a bank page action.
     *
     * @Route("/bank/edit/{id}", name="billbrother_bankaccount_bank_edit")
     */
    public function editAction($id)
    {
        return $this->render('BillBrotherBankAccountBundle:Bank:form.html.twig');
    }
}

关于版本:

✗ bin/console --version
Symfony version 3.1.1 - app/dev/debug

✗ vendor/bin/phpunit --version
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.


✗ php --version
PHP 7.0.6 (cli) (built: May 24 2016 03:29:56) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

我看到我的问题出在哪里了。在我的控制器中:

private function _buildForm(Request $request, Bank $bank)
{
    $form = $this->createForm(BankType::class, $bank);
    $form->handleRequest($request);

    if($form->isValid()) {
        $em = $this->getDoctrine()->getEntityManager();
        $em->persist($bank);
        $em->flush();
    }

    $form->add('submit', SubmitType::class, array(
        'label' => 'Create'
    ));
    return $form;
}

我们看到我在表单 handleRequest() 之后添加了 "submit" 按钮。通过移动代码:

    $form->add('submit', SubmitType::class, array(
        'label' => 'Create'
    ));

刚创建完表单,一切正常。

最终功能代码:

private function _buildForm(Request $request, Bank $bank)
{
    $form = $this->createForm(BankType::class, $bank);
    $form->add('submit', SubmitType::class, array(
        'label' => 'Create'
    ));
    $form->handleRequest($request);

    if($form->isValid()) {
        $em = $this->getDoctrine()->getEntityManager();
        $em->persist($bank);
        $em->flush();
    }

    return $form;
}