Key Takeaways
- In-memory databases, which exist only in the memory of the application, are a practical solution for testing code that interacts with a database. They are easy to set up with Symfony applications that use Doctrine and are great for testing due to their disposability.
- Symfony’s test environment configuration allows the creation of disposable test databases. The configuration file that needs to be edited to set up these databases is app/config/config_test.php. Using SQLite3’s support for in-memory databases can facilitate testing by sending SQL queries to a functioning database, removing the need to mock repository classes.
- When using the in-memory database in test classes, the schema must first be constructed, meaning the tables from the entities and any required fixtures must be loaded for the test suite. A database primer can be used to do most of this work, similar to forcefully running the doctrine schema update console command.
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 isapp/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 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.