Skip to content

Instantly share code, notes, and snippets.

@pavlakis
Created May 18, 2018 08:05
Show Gist options
  • Save pavlakis/863f12cb52360e4ab978b80891d7f806 to your computer and use it in GitHub Desktop.
Save pavlakis/863f12cb52360e4ab978b80891d7f806 to your computer and use it in GitHub Desktop.
Writing integration tests with fixtures for Symfony 3.4

Writing integration tests for Symfony 3.4

These are some of my notes on writing integration tests for Symfony 3.4 as it was a task which had some gotchas and took me some time to get it working.

Access to the container

In order to access the container of a Symfony project, our PHPUnit tests should extend KernelTestCase.

As per the documentation How to Test Doctrine Repositories, to retrieve the container, use the following:

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    /**
     * {@inheritDoc}
     */
    protected function setUp()
    {
        $kernel = self::bootKernel();

        $this->entityManager = $kernel->getContainer()
            ->get('doctrine')
            ->getManager();
    }
    

Accessing specific services

Note that since Symfony 3.3 on of the changes introduced is the services _default where if public: false is set, we won't be able to access services directly from the container.

That could make it problematic for integration tests since some services may not be accessible.

To get that working, make the value true in the config_test.yml file.

e.g.

services:
    _defaults:
        public: true

Fixtures

Use fixtures to insert known test data to the database before we run the tests.

Using the Doctrine Fixtures Bundle makes it easy to create fixtures.

The main issue I had was that an event listener calling postPersist was failing trying to access a method in a null project.

The problem in my case was that the Fixtures were being loaded in a different order and hence at that point that particular object didn't exist.

We can fix that by using the OrderedFixtureInterface

e.g.

class MyTableFixtures extends AbstractFixture implements OrderedFixtureInterface

And then return an integer with the order to which this should run by using the getOrder method.

e.g.

    /**
     * Get the order of this fixture
     *
     * @return integer
     */
    public function getOrder()
    {
        return 3;
    }

The special case of the User

The UserFixtures is special in that it needs to hash the password.

One way to do it is treating it as a Service as per the documentation.

The other way would be to retrieve it by the container by implementing ContainerAwareInterface.

We will also need:

    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

Clean and populate the database with the fixtures, and run phpunit:

bin/console doctrine:schema:drop --no-interaction --env=test --force
APP_ENV=test bin/console doctrine:schema:create --no-interaction --env=test
APP_ENV=test bin/console doctrine:fixtures:load --no-interaction --env=test
APP_ENV=test vendor/bin/phpunit --group integration

Organise integration tests

There are many ways to organise your tests and it varies if it is a new or existing project.

In my case it was a project with unit and acceptance tests hence I wanted a simple way to isolate the integration tests.

With PHPUnit annotations we can use the @group name to group the integration tests. It can either be on the method or the class.

/**
 * @group integration
 */
class MyTest extends extends KernelTestCase

Then on the command line to run only the integration tests, run:

APP_ENV=test vendor/bin/phpunit --group integration

And to exclude integration tests from the main suite, run:

vendor/bin/phpunit -c tests/phpunit.xml --testsuite unit --exclude-group integration

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment