Quick Tip: Testing Symfony Apps with a Disposable Database

Share this article

Quick Tip: Testing Symfony Apps with a Disposable Database
Testing code that interacts with a database can be a massive pain. Some developers mock database abstractions, and thus do not test the actual query. Others create a test database for the development environment, but this can also be a pain when it comes to continuous integration and maintaining the state of this database.
Symfony logo In-memory databases are an alternative to these options. As they exist only in the memory of the application, they are truly disposable and great for testing. Thankfully, these are very easy to set up with Symfony applications that use Doctrine. Try reading our guide to functional testing with Symfony after this to learn about testing the end-to-end behaviour of an application.

The Symfony Environment Configuration

One of the most powerful features of the Symfony framework is the ability to create different environments with their own unique configuration. This feature can be overlooked by Symfony developers, especially when it comes to the lesser known test environment that is investigated here. The Symfony guide to mastering and creating new environments explains how the framework handles the configuration for different environments and shows some useful examples. The configuration file that needs to be edited to set up disposable test databases is app/config/config_test.php
. When the application is accessed in the test suite, the kernel will be loaded using the test environment and this configuration file will be processed.

In-Memory Databases with Doctrine

SQLite3 supports in-memory databases which are great for testing. Using these, an application can be tested by actually sending SQL queries to a functioning database, removing the need to tediously mock repository classes with predefined behaviour. The database will be fresh at the start of the test and cleanly destroyed at the end. To override the default doctrine connection configuration, the lines below need to be added to the test environment configuration file. If there are multiple doctrine connections configured in the application, it may need to be adapted slightly to match.
# app/config/config_test.yml

doctrine:
    dbal:
        driver:  pdo_sqlite
        memory:  true
        charset: UTF8

Using the Database in Test Classes

When using this shiny new in-memory database in test classes, the schema must first be constructed. This means creating the tables from the entities and loading any fixtures that are required for the test suite. The class below can be used as a database primer that does most of this work. It has the same effect as forcefully running the doctrine schema update console command.
<?php

namespace Tests\AppBundle;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use Symfony\Component\HttpKernel\KernelInterface;

class DatabasePrimer
{
    public static function prime(KernelInterface $kernel)
    {
        // Make sure we are in the test environment
        if ('test' !== $kernel->getEnvironment()) {
            throw new \LogicException('Primer must be executed in the test environment');
        }

        // Get the entity manager from the service container
        $entityManager = $kernel->getContainer()->get('doctrine.orm.entity_manager');

        // Run the schema update tool using our entity metadata
        $metadatas = $entityManager->getMetadataFactory()->getAllMetadata();
        $schemaTool = new SchemaTool($entityManager);
        $schemaTool->updateSchema($metadatas);

        // If you are using the Doctrine Fixtures Bundle you could load these here
    }
}
If the entity manager is required to test a class, the primer must be applied:
<?php

namespace Tests\AppBundle;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Tests\AppBundle\DatabasePrimer;

class FooTest extends KernelTestCase
{
    public function setUp()
    {
        self::bootKernel();

        DatabasePrimer::prime(self::$kernel);
    }

    public function testFoo()
    {
        $fooService = self::$kernel->getContainer()->get('app.foo_service');

        // ...
    }
}
In the example above the container is used to get the service that is being tested. If this service depends on the entity manager, it will be constructed with the same entity manager that is primed in the setUp method. If more control is required, maybe to mock another dependency, the entity manager can always be retrieved from the container and used to manually instantiate the class that needs testing. It may also be a good idea to use the Doctrine Fixtures Bundle to populate the database with test data, but this will depend on your use case.

Frequently Asked Questions (FAQs) about Testing Symfony Apps with a Disposable Database

What is the purpose of testing Symfony apps with a disposable database?

The main purpose of testing Symfony apps with a disposable database is to ensure that the application works as expected without affecting the production database. This method allows developers to perform various tests, including unit tests, functional tests, and integration tests, without the risk of corrupting or losing important data. It provides a safe environment where developers can freely experiment and debug their applications.

How do I set up a disposable database for Symfony testing?

Setting up a disposable database for Symfony testing involves several steps. First, you need to configure your environment variables in the .env file to connect to your database. Then, you need to create a new database for testing purposes. This can be done using the Doctrine command-line interface. After creating the database, you can use Doctrine’s schema tool to create the necessary tables and relationships based on your Symfony entities.

What are the benefits of using a disposable database for Symfony testing?

Using a disposable database for Symfony testing offers several benefits. It allows you to isolate your tests from your production environment, ensuring that your tests do not affect your live data. It also makes it easier to replicate bugs and issues, as you can recreate the exact state of your application at the time of the error. Additionally, it allows you to test your database interactions, ensuring that your queries and transactions work as expected.

Can I use a disposable database for testing other PHP frameworks?

Yes, you can use a disposable database for testing other PHP frameworks. The concept of a disposable database is not exclusive to Symfony. It can be applied to any framework or language that interacts with a database. The specific implementation may vary depending on the framework, but the general idea remains the same.

How do I handle database migrations when testing with a disposable database?

When testing with a disposable database, you can handle database migrations using Doctrine’s migration tools. These tools allow you to generate migration classes based on the changes in your entities. You can then execute these migrations to update your test database schema. This ensures that your test database is always in sync with your application’s database schema.

How can I ensure that my tests are isolated when using a disposable database?

To ensure that your tests are isolated when using a disposable database, you should reset the state of your database before each test. This can be done by rolling back any changes made during the test, or by dropping and recreating the database before each test. Symfony provides tools and features that make it easy to reset your database state, ensuring that each test runs in a clean environment.

What are the best practices for testing Symfony apps with a disposable database?

Some of the best practices for testing Symfony apps with a disposable database include isolating your tests, resetting your database state before each test, and using database migrations. It’s also important to write comprehensive tests that cover all aspects of your application, including database interactions. Additionally, you should aim to keep your tests as simple and readable as possible, to make it easier for other developers to understand and maintain your tests.

Can I use a disposable database for performance testing?

Yes, you can use a disposable database for performance testing. However, keep in mind that the performance of a disposable database may not accurately reflect the performance of your production database. This is because a disposable database is typically smaller and less complex than a production database. Therefore, while it can be useful for identifying performance issues, it should not be the only tool you use for performance testing.

How do I debug issues when testing with a disposable database?

Debugging issues when testing with a disposable database can be done using various tools and techniques. Symfony provides a debug toolbar that provides detailed information about your application, including database queries. You can also use logging and exception handling features to track errors and issues. Additionally, you can use PHPUnit’s debugging features to step through your code and inspect variables.

Can I use a disposable database for testing Symfony apps in a continuous integration environment?

Yes, you can use a disposable database for testing Symfony apps in a continuous integration environment. In fact, it’s highly recommended to do so. By using a disposable database, you can ensure that your tests are isolated and repeatable, which is crucial in a continuous integration environment. You can automate the process of creating and setting up the disposable database as part of your build process, ensuring that your tests always run against a clean database.

Andrew CarterAndrew Carter
View Author

Andrew is a software developer from the United Kingdom with a Master's Degree in Physics. He's the Head of Technical Development at MoneyMaxim, a developer of open source software and an up-and-coming speaker. In his spare time he dabbles in photography and practices Muay Thai.

databaseOOPHPPHPPHP Quick Tipsquick-tipsqlitesqlite3symfonysymfony frameworksymfony2symfony3tddTesting
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week