Forms 当Symfony2表单使用链接到数据库的转换器时,如何对其进行单元测试

Forms 当Symfony2表单使用链接到数据库的转换器时,如何对其进行单元测试,forms,unit-testing,symfony,phpunit,transformer,Forms,Unit Testing,Symfony,Phpunit,Transformer,TLDR:我不熟悉单元测试,我有几个问题: 我的变压器测试写得好吗 有没有办法将我的transformer测试与数据库分离 如何使用数据库使用transformer测试我的表单 我是否应该将表单与转换器分离 我不知道我的类是否过于耦合,我的设计是否有缺陷,或者我对单元测试的理解是否糟糕 以下是一些背景资料。 我有一个带有不同小部件的表单对象。其中一个用于模型变压器中。 此模型转换器使用到数据库的连接来检索适当的对象 这是我的密码: class BookToStringTransformer i

TLDR:我不熟悉单元测试,我有几个问题:

  • 我的变压器测试写得好吗
  • 有没有办法将我的transformer测试与数据库分离
  • 如何使用数据库使用transformer测试我的表单
  • 我是否应该将表单与转换器分离

  • 我不知道我的类是否过于耦合,我的设计是否有缺陷,或者我对单元测试的理解是否糟糕

    以下是一些背景资料。
    我有一个带有不同小部件的表单对象。其中一个用于模型变压器中。
    此模型转换器使用到数据库的连接来检索适当的对象

    这是我的密码:

    class BookToStringTransformer implements DataTransformerInterface {
    
        private $om;
    
        public function __construct(ObjectManager $om) {
            $this->om = $om;
        }
    
        public function transform($book) {
            if (!$book instanceof Book) {
                return "";
            }
    
            return $book->getName();
        }
    
        public function reverseTransform($string) {
            if (!is_string($string) || !$string) {
                return null;
            }
    
            $book = $this->om
                    ->getRepository('MyBundle:Book')
                    ->findOneBy(array('name' => $string))
            ;
    
            if (null === $book) {
                throw new TransformationFailedException(sprintf(
                        'The book "%s" does not exist!', $string
                ));
            }
    
            return $book;
        }
    
    }
    
    
    class ItemType extends AbstractType {
    
        private $om;
    
        public function __construct(ObjectManager $om) {
            $this->om = $om;
        }
    
        public function buildForm(FormBuilderInterface $builder, array $options) {
            $bookTransformer = new BookToStringTransformer($this->om);
            $builder->add($builder->create('book', 'text', array(
                        'required' => false,
                    ))->addModelTransformer($bookTransformer));
        }
    
        public function setDefaultOptions(OptionsResolverInterface $resolver) {
            $resolver->setDefaults(array(
                'data_class' => 'MyBundle\Entity\Item',
            ));
    
        }
    
        public function getName() {
            return 'mybundle_item';
        }
    
    }
    
    我使用KernelTestCase编写了变压器的单元测试

    class BookToStringTransformerTest extends KernelTestCase {
    
        private $name = 'existing name';
        private $em;
    
        public function setUp() {
            static::$kernel = static::createKernel();
            static::$kernel->boot();
            $this->em = static::$kernel->getContainer()
                    ->get('doctrine')
                    ->getManager();
        }
    
        public function testReverseTransform_whenNameExists_returnsBookObject() {
            $transformer = new BookToStringTransformer($this->em);
            $book = $transformer->reverseTransform($this->name);
            $this->assertInstanceOf('MyBundle\Entity\Book', $book, 'Should return a Book object');
            $this->assertEquals($this->name, $book->getName(), 'Should return a Book object with the selected name');
        }
    
        /**
         * @expectedException Symfony\Component\Form\Exception\TransformationFailedException
         */
        public function testReverseTransform_whenNameDoesNotExist_throwsException() {
            $transformer = new BookToStringTransformer($this->em);
            $transformer->reverseTransform('unknown name');
        }
    
        /**
         * @param mixed $invalid_parameter
         * @dataProvider provideInvalidParameter
         */
        public function testReverseTransform_whenParameterIsInvalid_returnsNull($invalid_parameter) {
            $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
            $transformer = new BookToStringTransformer($om);
            $this->assertNull($transformer->reverseTransform($invalid_parameter), 'Should return a NULL value');
        }
    
        /**
         * @return array
         */
        public function provideInvalidParameter() {
            return [
                [null],
                [false],
                [true],
                [''],
                [[]],
                [new \stdClass()],
            ];
        }
    
        public function testTransform_whenParameterIsBookObject_returnsName() {
            $book = $this->em->getRepository('MyBundle:Book')
                    ->findOneBy(array('name' => $this->name));
    
            $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
            $transformer = new BookToStringTransformer($om);
            $this->assertEquals($this->name, $transformer->transform($book), 'Should return a string containing the name');
        }
    
        /**
         * @param mixed $not_book
         * @dataProvider provideInvalidBookObject
         */
        public function testTransform_whenParameterIsNotBookObject_returnsEmptyString($not_book) {
            $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
            $transformer = new BookToStringTransformer($om);
            $this->assertEquals("", $transformer->transform($not_book), 'Should return an empty string to be chained');
        }
    
        /**
         * @return array
         */
        public function provideInvalidBookObject() {
            return [
                [null],
                [123],
                ['123'],
                [[]],
                [true],
                [new \stdClass()],
            ];
        }
    
    }
    
    由于我不熟悉单元测试,我甚至不知道这是否是测试变压器的正确方法。
    我开始为表单对象编写测试。我使用的是TypeTestCase,但是没有简单的方法可以连接到数据库,我不能使用KernelTestCase

    class ItemTypeTest extends TypeTestCase {
    
        /**
         * @expectedException \PHPUnit_Framework_Error
         */
        public function test_whenCreatedWithNoParameters_raiseException() {
            new ItemType();
        }
    
        /**
         * @expectedException \PHPUnit_Framework_Error
         */
        public function test_whenCreatedWithBadParameters_raiseException() {
            new ItemType(123);
        }
    
        public function test_whenCreatedWithGoodParameters_createsFormObject() {
            $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
            $type = new ItemType($om);
            $form = $this->factory->create($type);
            $this->assertInstanceOf('Symfony\Component\Form\Form', $form);
        }
    
        public function test_whenSubmittedWithGoodData() {
            $formData = array(
                'name' => 'existing name',
            );
    
            $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
            $type = new ItemType($om);
            $form = $this->factory->create($type);
    
            $form->submit($formData);
        }
    
    }
    
    最后一个测试失败,因为我正在向表单传递一个模拟,所以转换器确实可以访问数据库。所以我应该得到一个真实的对象(意味着类太耦合了),还是应该找到另一种方法呢


    谢谢

    方法很好,在最后一种方法中,必须模拟回购对象和回购响应。在示例中,请尝试以下代码:

    public function test_whenSubmittedWithGoodData() {
            $formData = array(
                'name' => 'existing name',
            );
    
           $om = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')->getMock();
    
            $repoMock= $this->getMock('Doctrine\ORM\EntityRepository', array(), array(), '', false);
    
    
             $om
                ->expects($this->atLeastOnce())
                ->method('getRepository')
                ->withAnyParameters()
                ->will($this->returnValue($repoMock));
    
    
            $repoMock
                ->expects($this->atLeastOnce())
                ->method('findOneBy')
                ->withAnyParameters()
                ->will($this->returnValue($mockedBook));
    
            $type = new ItemType($om);
            $form = $this->factory->create($type);
    
            $form->submit($formData);
        }
    

    谢谢你的快速回复。据我所知,$mockedBook也是一个模拟对象。我在这一点上得到什么并不重要。我说的对吗?取决于您的测试范围,在您的情况下,您不会对返回对象进行任何检查。您可以模拟repo找不到带有$repoMock->expects($this->atlestOnce())->方法('findOneBy')->withAnyParameters()->will($this->returnValue(null))的书籍;美好的基本上,我隐藏了我的book对象实现的所有细节,只返回有趣的部分。