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;
}
我在 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;
}