Outils pour utilisateurs

Outils du site


pele_mele:stack_exchange:stack_overflow:27801219

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentesRévision précédente
pele_mele:stack_exchange:stack_overflow:27801219 [2024/11/23 03:57] – supprimée - modification externe (Date inconnue) 127.0.0.1pele_mele:stack_exchange:stack_overflow:27801219 [2024/11/23 03:57] (Version actuelle) – ↷ Page déplacée et renommée de pele_mele:stack_exchange:stackoverflow-27801219 à pele_mele:stack_exchange:stack_overflow:27801219 alexis
Ligne 1: Ligne 1:
 +====== How to unit test a Symfony2 form when it uses a transformer linked to a database ======
 +
 +TLDR: I am new to unit tests and I have few questions:
 +
 +  - Are my transformer tests well written?
 +  - Is there a way to decoupled my transformer tests from the database?
 +  - How to test my form with the transformer using the database?
 +  - Should I decouple my form from my transformer?
 +
 +-------
 +I don't know if my classes are too coupled, if my design is flawed or if my understanding of the unit tests is bad.
 +
 +Here is some background.  
 +I have a form object with different widgets. One of them is used within a model transformer.  
 +This model transformer uses a connection to the database to retrieve the proper object.
 +
 +Here is my code:
 +
 +<code php>
 +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';
 +    }
 +}
 +</code>
 +
 +I wrote unit tests for the transformer using the KernelTestCase
 +
 +<code php>
 +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()],
 +        ];
 +    }
 +}
 +</code>
 +
 +As I am new to unit tests, I don't even know if it is the proper way to test that transformer.  
 +I start writing tests for the form object. I am using the TypeTestCase but there is no simple way to get the connection to the database and I can't use the KernelTestCase.
 +
 +<code php>
 +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);
 +    }
 +}
 +</code>
 +
 +The last test fails because the transformer does get access to the database since I am passing a mock to the form. So should I get a real object (meaning classes are too coupled) or should I find an other way.
 +
 +Thank you
 +<WRAP help>
 +The approach is good, in the last method you must mock the repo object and the repo response. In example try this code:
 +<code php>
 +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);
 +}
 +</code>
 +</WRAP>
 +<WRAP info>
 +[[https://stackoverflow.com/questions/27801219/how-to-unit-test-a-symfony2-form-when-it-uses-a-transformer-linked-to-a-database|How to unit test a Symfony2 form when it uses a transformer linked to a database - Stack Overflow]]
 +</WRAP>