【问题标题】:How to unit test Symfony 4 Form with EntityType field如何使用 EntityType 字段对 Symfony 4 表单进行单元测试
【发布时间】:2018-09-27 05:07:38
【问题描述】:

如何使用 EntityType 字段对 Symfony 4 表单进行单元测试

当我运行测试时:

$ ./vendor/bin/simple-phpunit tests/Unit/Form/ProductFormTest.php

这是我终端的输出:

Sebastian Bergmann 的 PHPUnit 6.5.8

和贡献者。

运行时:PHP 7.2.4-1+ubuntu16.04.1+deb.sury.org+1 和 Xdebug 2.7.0alpha2-dev 配置:/var/www/project/phpunit.xml.dist

测试应用\Tests\Unit\Form\ProductFormTest E
1 / 1 (100%)

时间:551 毫秒,内存:6.00MB

有 1 个错误:

1) App\Tests\Unit\Form\ProductFormTest::formSubmitsValidData Symfony\Component\Form\Exception\RuntimeException: 类 “App\Entity\Supplier”似乎不是一个托管的 Doctrine 实体。做过 你忘了映射吗?

/var/www/project/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:205 /var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:858 /var/www/project/vendor/symfony/doctrine-bridge/Form/Type/DoctrineType.php:130 /var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:766 /var/www/project/vendor/symfony/options-resolver/OptionsResolver.php:698 /var/www/project/vendor/symfony/form/ResolvedFormType.php:95 /var/www/project/vendor/symfony/form/FormFactory.php:76 /var/www/project/vendor/symfony/form/FormBuilder.php:97 /var/www/project/vendor/symfony/form/FormBuilder.php:256 /var/www/project/vendor/symfony/form/FormBuilder.php:206 /var/www/project/vendor/symfony/form/FormFactory.php:30 /var/www/project/tests/Unit/Form/ProductFormTest.php:86

错误!测试:1,断言:0,错误:1。

这个错误是在模拟 ManagerRegistry 类之后开始的。似乎在这个单元测试中没有针对存在的学说实体的映射。

有没有一种简洁的方法来测试带有“Symfony\Bridge\Doctrine\Form\Type\EntityType”字段的表单?

src\App\Entity\Product.php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use App\Entity\Supplier;

/**
 * Product Entity
 * 
 * @ORM\Entity(repositoryClass = "App\Repository\ProductRepository")
 * @ORM\Table(name = "product")
 */
class Product
{
    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();

        $this->setType(AbstractProduct::TYPE_PARENT);
    }

    /**
     * To String
     * 
     * @return string
     */
    public function __toString()
    {
        return "[" . $this->id . "] Product: " . $this->ean . " | " . $this->name;
    }

    /**
     * ID
     *
     * @var integer
     *
     * @ORM\Id
     * @ORM\Column(name = "product_id", type = "integer")
     * @ORM\GeneratedValue(strategy = "AUTO")
     */
    protected $id;

    /**
     * EAN (European Article Number)
     *
     * @var string
     *
     * @ORM\Column(name = "product_ean", type = "string", length = 13)
     */
    protected $ean;

    /**
     * Name
     *
     * @var string
     *
     * @ORM\Column(name = "product_name", type = "string", length = 128)
     */
    protected $name;

    /**
     * Description
     *
     * @var string
     *
     * @ORM\Column(name = "product_description", type = "text", nullable = true)
     */
    protected $description;

    /**
     * Supplier
     *
     * Many Products have one Supplier
     *
     * @var Supplier
     *
     * @ORM\ManyToOne(targetEntity = "Supplier", inversedBy = "products")
     * @ORM\JoinColumn(name = "supplier_id", referencedColumnName = "supplier_id")
     */
    protected $supplier;

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

    /**
     * Set ean
     *
     * @param string $ean
     *
     * @return AbstractProduct
     */
    public function setEan($ean)
    {
        $this->ean = $ean;

        return $this;
    }

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

    /**
     * Set name
     *
     * @param string $name
     *
     * @return AbstractProduct
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

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

    /**
     * Set description
     *
     * @param string $description
     *
     * @return AbstractProduct
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

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

    /**
     * Set supplier
     *
     * @param \App\Entity\Supplier $supplier
     *
     * @return Product
     */
    public function setSupplier(Supplier $supplier = null)
    {
        $this->supplier = $supplier;

        return $this;
    }

    /**
     * Get supplier
     *
     * @return \App\Entity\Supplier
     */
    public function getSupplier()
    {
        return $this->supplier;
    }
}

src\App\Form\ProductForm.php

namespace App\Form;

use App\Entity\Supplier;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;

class ProductForm extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $translationDomain = "product";

        /*
         * Card
         */
        $builder->add("ean", TextType::class, [
            "label"              => "product.ean",
            "required"           => true,
            "translation_domain" => $translationDomain,
        ]);

        $builder->add("name", TextType::class, [
            "label"              => "product.name",
            "required"           => true,
            "translation_domain" => $translationDomain,
        ]);

        $builder->add("supplier", EntityType::class, [
            "class"              => Supplier::class,
            "choice_label"       => "name",
            "label"              => "supplier.name",
            "required"           => false,
            "translation_domain" => "supplier",
        ]);
    }
}

测试\Unit\Form\ProductFormTest.php

namespace App\Tests\Unit\Form;

use App\Entity\Product;
use App\Form\ProductForm;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Bridge\Doctrine\ManagerRegistry;
use Symfony\Component\Form\PreloadedExtension;
use Symfony\Component\Form\Test\TypeTestCase;

class ProductFormTest extends TypeTestCase
{
    /**
     * @var ManagerRegistry
     */
    private $_managerRegistry;

    /**
     * {@inheritdoc}
     */
    protected function setUp()
    {
        $this->_managerRegistry = $this->createMock(ManagerRegistry::class);

        parent::setUp();
    }

    /**
     * {@inheritdoc}
     */
    protected function tearDown()
    {
        $this->_managerRegistry = null;

        parent::tearDown();
    }

    /**
     * {@inheritdoc}
     */
    protected function getExtensions()
    {
        $entityType = new EntityType($this->_managerRegistry);

        return [
            new PreloadedExtension([$entityType], [])
        ];
    }

    /**
     * @test
     */
    public function formSubmitsValidData()
    {
        $createdAt = new \DateTime();

        $formData = [
            "ean"         => "8718923400440",
            "name"        => "Plumbus",
            "description" => "This is a household device so common it does not need an introduction",
        ];

        $productComparedToForm = new Product();
        $productComparedToForm
            ->setEan($formData["ean"])
            ->setName($formData["name"])
        ;

        $productHandledByForm = new Product();

        $form = $this->factory->create(ProductForm::class, $productHandledByForm);

        $form->submit($formData);

        static::assertTrue($form->isSynchronized());
        static::assertEquals($productComparedToForm, $productHandledByForm);

        $view = $form->createView();

        foreach (array_keys($formData) as $key) {
            static::assertArrayHasKey($key, $view->children);
        }
    }
}

【问题讨论】:

    标签: forms unit-testing symfony symfony4


    【解决方案1】:

    首先你的测试用例应该从Symfony\Component\Form\Test\TypeTestCase扩展。

    那么你的测试应该是这样的:

    // Example heavily inspired by EntityTypeTest inside the Symfony Bridge
    class ProductTypeTest extends TypeTestCase
    {
        /**
         * @var EntityManager
         */
        private $em;
    
        /**
         * @var \PHPUnit_Framework_MockObject_MockObject|ManagerRegistry
         */
        private $emRegistry;
    
        protected function setUp()
        {
            $this->em = DoctrineTestHelper::createTestEntityManager();
            $this->emRegistry = $this->createRegistryMock('default', $this->em);
    
            parent::setUp();
    
            $schemaTool = new SchemaTool($this->em);
    
            // This is the important part for you !
            $classes = [$this->em->getClassMetadata(Supplier::class)];
    
            try {
                $schemaTool->dropSchema($classes);
            } catch (\Exception $e) {
            }
    
            try {
                $schemaTool->createSchema($classes);
            } catch (\Exception $e) {
            }
        }
        protected function createRegistryMock($name, $em)
        {
            $registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')->getMock();
            $registry->expects($this->any())
                ->method('getManager')
                ->with($this->equalTo($name))
                ->will($this->returnValue($em));
    
            return $registry;
        }
    
        protected function getExtensions()
        {
            return array_merge(parent::getExtensions(), array(
                new DoctrineOrmExtension($this->emRegistry),
            ));
        }
    
        protected function tearDown()
        {
            parent::tearDown();
    
            $this->em = null;
            $this->emRegistry = null;
        }
    }
    

    【讨论】:

    • 你能告诉我 $this->createRegistryMock() 方法中发生了什么吗?
    • 这会导致另一个错误:ArgumentCountError: Too little arguments to function Doctrine\Common\Persistence\AbstractManagerRegistry::__construct(), 0 pass
    • @Nek : 测试应该扩展 TypeTestCase 就像在 OP 和 SF 文档中一样 => symfony.com/doc/current/form/unit_testing.html
    • 你是绝对正确的。我的错。下次请考虑编辑否决票;-)。
    • 这导致“类“App\Entity\Field”似乎不是托管的 Doctrine 实体。您忘记映射了吗?” (Field::class 是 EntityTyp 中使用的实体,如上例中的 Supplier::class)。
    【解决方案2】:

    如果上述解决方案不起作用,则另一种解决方案。另一个应用程序的示例。

    namespace App\Tests\Form;
    
    use App\Entity\BusinessDepartment;
    use App\Entity\Contact;
    use App\Form\ContactForm;
    use Doctrine\ORM\AbstractQuery;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\EntityRepository;
    use Doctrine\ORM\Mapping\ClassMetadata;
    use Doctrine\ORM\QueryBuilder;
    use Doctrine\Persistence\ManagerRegistry;
    use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
    use Symfony\Component\Form\Test\TypeTestCase;
    
    class ContactFormTest extends TypeTestCase
    {
        protected function getExtensions() {
    
            $mockEntityManager = $this->createMock(EntityManager::class);
            $mockEntityManager->method('getClassMetadata')
                ->willReturn(new ClassMetadata(BusinessDepartment::class))
            ;
    
            $execute = $this->createMock(AbstractQuery::class);
            $execute->method('execute')
                ->willReturn([]);
    
            $query = $this->createMock(QueryBuilder::class);
            $query->method('getQuery')
                ->willReturn($execute);
    
    
            $entityRepository = $this->createMock(EntityRepository::class);
            $entityRepository->method('createQueryBuilder')
                ->willReturn($query)
            ;
    
            $mockEntityManager->method('getRepository')->willReturn($entityRepository);
    
            $mockRegistry = $this->createMock(ManagerRegistry::class);
            $mockRegistry->method('getManagerForClass')
                ->willReturn($mockEntityManager)
            ;
    
          return array_merge(parent::getExtensions(), [new DoctrineOrmExtension($mockRegistry)]);
       }
    
        public function testBuildForm()
        {
            $data = [
                'name' => 'nameTest',
                'firstName' => 'firstnameTest',
                'email' => 'test_email@gmail.com',
                'message' => 'messageTest'
                ];
    
            $contact = new Contact();
    
            $form = $this->factory->create( ContactForm::class, $contact);
    
            $contactToCompare = new Contact();
    
            $contactToCompare->setName($data['name']);
            $contactToCompare->setFirstName($data['firstName']);
            $contactToCompare->setEmail($data['email']);
            $contactToCompare->setMessage($data['message']);
    
            //check the submission
            $form->submit($data);
    
            $this->assertTrue($form->isSynchronized());
    
            $this->assertEquals($contact->getName(), $contactToCompare->getName());
            $this->assertEquals($contact->getFirstName(), $contactToCompare->getFirstName());
            $this->assertEquals($contact->getEmail(), $contactToCompare->getEmail());
            $this->assertEquals($contact->getMessage(), $contactToCompare->getMessage());
        }
    }
    

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    【解决方案3】:

    如果上述解决方案不起作用,则另一种解决方案。另一个应用程序的示例。

    ContactFromTest.php

    namespace App\Tests\Form;
    
    use App\Entity\BusinessDepartment;
    use App\Entity\Contact;
    use App\Form\ContactForm;
    use Doctrine\ORM\AbstractQuery;
    use Doctrine\ORM\EntityManager;
    use Doctrine\ORM\EntityRepository;
    use Doctrine\ORM\Mapping\ClassMetadata;
    use Doctrine\ORM\QueryBuilder;
    use Doctrine\Persistence\ManagerRegistry;
    use Symfony\Bridge\Doctrine\Form\DoctrineOrmExtension;
    use Symfony\Component\Form\Test\TypeTestCase;
    
    class ContactFormTest extends TypeTestCase
    {
        protected function getExtensions() {
    
            $mockEntityManager = $this->createMock(EntityManager::class);
            $mockEntityManager->method('getClassMetadata')
                ->willReturn(new ClassMetadata(BusinessDepartment::class))
            ;
    
            $execute = $this->createMock(AbstractQuery::class);
            $execute->method('execute')
                ->willReturn([]);
    
            $query = $this->createMock(QueryBuilder::class);
            $query->method('getQuery')
                ->willReturn($execute);
    
    
            $entityRepository = $this->createMock(EntityRepository::class);
            $entityRepository->method('createQueryBuilder')
                ->willReturn($query)
            ;
    
            $mockEntityManager->method('getRepository')->willReturn($entityRepository);
    
            $mockRegistry = $this->createMock(ManagerRegistry::class);
            $mockRegistry->method('getManagerForClass')
                ->willReturn($mockEntityManager)
            ;
    
          return array_merge(parent::getExtensions(), [new DoctrineOrmExtension($mockRegistry)]);
       }
    
        public function testBuildForm()
        {
            $data = [
                'name' => 'nameTest',
                'firstName' => 'firstnameTest',
                'email' => 'test_email@gmail.com',
                'message' => 'messageTest'
                ];
    
            $contact = new Contact();
    
            $form = $this->factory->create( ContactForm::class, $contact);
    
            $contactToCompare = new Contact();
    
            $contactToCompare->setName($data['name']);
            $contactToCompare->setFirstName($data['firstName']);
            $contactToCompare->setEmail($data['email']);
            $contactToCompare->setMessage($data['message']);
    
            //check the submission
            $form->submit($data);
    
            $this->assertTrue($form->isSynchronized());
    
            $this->assertEquals($contact->getName(), $contactToCompare->getName());
            $this->assertEquals($contact->getFirstName(), $contactToCompare->getFirstName());
            $this->assertEquals($contact->getEmail(), $contactToCompare->getEmail());
            $this->assertEquals($contact->getMessage(), $contactToCompare->getMessage());
        }
    }
    

    ContacForm.php

    class ContactForm extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('name',TextType::class, [
                    'label' => 'Nom'
                ])
                ->add('firstName',TextType::class, [
                    'label' => 'Prénom'
                ])
                ->add('email', EmailType::class, [
                    'label' => 'Email'
                ])
                ->add('businessDepartment', EntityType::class, [
                    'label' => 'Département à contacter',
                    'class' => BusinessDepartment::class,
                    'choice_value' => 'id',
                    'choice_label' => 'nameDepartment',
                ])
                ->add('message', TextareaType::class, [
                    'label' => 'Votre message'
                ])
            ;
        }
    }
    

    【讨论】:

    • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-25
    • 1970-01-01
    • 2018-09-19
    • 2013-09-13
    • 2012-01-08
    相关资源
    最近更新 更多