Skip to content

[WIP] Adding a testing/database cookbook #2407

Merged
merged 2 commits into from Apr 26, 2013

4 participants

@Sgoettschkes

This PR adds a new cookbook for testing with a database involved.

Q A
Doc fix? no
New docs? yes
Applies to 2.0+
Fixed tickets #480

This PR is not ready for merge yet

@WouterJ WouterJ commented on the diff Mar 30, 2013
cookbook/testing/database.rst
+
+ public function __construct(ObjectManager $entityManager)
+ {
+ $this->entityManager = $entityManager;
+ }
+
+ public function calculateTotalSalary($id)
+ {
+ $employeeRepository = $this->entityManager->getRepository('AcmeDemoBundle::Employee');
+ $employee = $userRepository->find($id);
+
+ return $employee->getSalary() + $employee->getBonus();
+ }
+ }
+
+As the ``ObjectManager`` gets injected into the class through the constructor, it's
@WouterJ
Symfony member
WouterJ added a note Mar 30, 2013

Use some api link here

@stof
Symfony member
stof added a note Mar 30, 2013

@WouterJ API links don't work for Doctrine interfaces

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@WouterJ WouterJ commented on an outdated diff Mar 30, 2013
cookbook/testing/database.rst
+ ``__call``, you cannot define a mock method on ``find()`` but must instead use
+ ``__call``. It's possible to implement the ``find()`` method in your ``Repository``
+ and use it when mocking. In this case, make sure to mock your ``Repository``
+ class instead of the generic ``EntityRepository``.
+
+Changing database settings for functional tests
+-----------------------------------------------
+
+If you have functional tests, you want them to interact with a real database.
+Most of the time you want to use a dedicated database connection to make sure
+not to overwrite data you entered when developing the application and also
+to be able to clear the database before every test.
+
+To do this, you can specify the database configuration inside your ``app/config/config_test.yml``::
+
+ doctrine:
@WouterJ
Symfony member
WouterJ added a note Mar 30, 2013

should be:

[...] inside your ``app/config/config_test.yml``:

.. code-block:: yaml

    doctrine:
        # ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@WouterJ WouterJ commented on the diff Mar 30, 2013
cookbook/testing/database.rst
+Changing database settings for functional tests
+-----------------------------------------------
+
+If you have functional tests, you want them to interact with a real database.
+Most of the time you want to use a dedicated database connection to make sure
+not to overwrite data you entered when developing the application and also
+to be able to clear the database before every test.
+
+To do this, you can specify the database configuration inside your ``app/config/config_test.yml``::
+
+ doctrine:
+ dbal:
+ host: localhost
+ dbname: testdb
+ user: testdb
+ password: testdb
@WouterJ
Symfony member
WouterJ added a note Mar 30, 2013

Please add other formats as well

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on an outdated diff Mar 30, 2013
cookbook/testing/database.rst
+
+ class SalaryCalculatorTest extends \PHPUnit_Framework_TestCase
+ {
+
+ public function testCalculateTotalSalary()
+ {
+ // First, mock the object to be used in the test
+ $employee = $this->getMock('\Acme\DemoBundle\Entity\Employee');
+ $employee->expects($this->once())
+ ->method('getSalary')
+ ->will($this->returnValue(1000));
+ $employee->expects($this->once())
+ ->method('getBonus')
+ ->will($this->returnValue(1100));
+
+ // Now, mock tthe repository so it returns the mock of the employee
@stof
Symfony member
stof added a note Mar 30, 2013

typo: the

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on an outdated diff Mar 30, 2013
cookbook/testing/database.rst
+ ->method('getSalary')
+ ->will($this->returnValue(1000));
+ $employee->expects($this->once())
+ ->method('getBonus')
+ ->will($this->returnValue(1100));
+
+ // Now, mock tthe repository so it returns the mock of the employee
+ $employeeRepository = $this->getMockBuilder('\Doctrine\ORM\EntityRepository')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $employeeRepository->expects($this->once())
+ ->method('__call')
+ ->will($this->returnValue($employee));
+
+ // Last, mock the EntityManager to return the mock of the repository
+ $entityManager = $this->getMockBuilder('\Doctrine\ORM\EntityManager')
@stof
Symfony member
stof added a note Mar 30, 2013

As your typehint is the ObjectManager, I would mock the ObjectManager interface only

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on an outdated diff Mar 30, 2013
cookbook/testing/database.rst
+ {
+ // First, mock the object to be used in the test
+ $employee = $this->getMock('\Acme\DemoBundle\Entity\Employee');
+ $employee->expects($this->once())
+ ->method('getSalary')
+ ->will($this->returnValue(1000));
+ $employee->expects($this->once())
+ ->method('getBonus')
+ ->will($this->returnValue(1100));
+
+ // Now, mock tthe repository so it returns the mock of the employee
+ $employeeRepository = $this->getMockBuilder('\Doctrine\ORM\EntityRepository')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $employeeRepository->expects($this->once())
+ ->method('__call')
@stof
Symfony member
stof added a note Mar 30, 2013

You test uses find, not a method going through the __call implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on an outdated diff Mar 30, 2013
cookbook/testing/database.rst
+ $entityManager->expects($this->once())
+ ->method('getRepository')
+ ->will($this->returnValue($employeeRepository));
+
+ $salaryCalculator = new SalaryCalculator($entityManager);
+ $this->assertEquals(1100, $salaryCalculator->calculateTotalSalary(1));
+ }
+ }
+
+We are building our mocks from the inside out, first creating the employee which
+gets returned by the ``Repository`` which itself gets returned by the ``EntityManager``.
+This way, no real class is involved in testing.
+
+.. note::
+
+ As a ``Repository`` doesn't have a ``find()`` method but uses the magic method
@stof
Symfony member
stof added a note Mar 30, 2013

Wrong. find is not a magic method (it is even part of the common ObjectRepository interface)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@weaverryan weaverryan merged commit 53174e0 into symfony:2.0 Apr 26, 2013
@weaverryan
Symfony member

Hey Sebastian!

I've merged this in now, since it already solves much of #480. I only had minor tweaks at sha: 0506361. This is a very nice entry.

Thanks!

@Sgoettschkes Sgoettschkes deleted the unknown repository branch Apr 27, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.