Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Test Fixtures & Fast Databases!

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $10.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

In practice, the hardest thing about functional testing isn't all the stuff about clicking links or filling out forms. Nope, the toughest thing is taking control of the database!

To Fixture or Not to Fixture?

To do this, there are two big philosophies! First, like with integration tests, we can decide to always start the database empty. And then, if we need data - like we need to add some Enclosures to the database - we will add that data inside the test method itself. This is how I normally code. It's not super fancy, and it means that you need to do extra work in each test to create the exact data you need. But, it also means that each test reads like a complete story. For example, at the top of this test, you would be able to see that we created 3 Enclosure objects. Then, at the bottom, it will make sense why we're expecting to see 3 rows in the table.

The second philosophy, which is a bit simpler, is to load data fixtures. This is what we're going to do: but I'll mention how things would be different if you want to use the first philosophy.

Adding Data Fixtures

First, install the DoctrineFixturesBundle:

composer require --dev doctrine/doctrine-fixtures-bundle:2.4.1

If you downloaded the course code, in the tutorial/ directory, you should have a DataFixtures directory. Copy that into your AppBundle.

... lines 1 - 10
class LoadBasicParkData extends AbstractFixture implements OrderedFixtureInterface
public function load(ObjectManager $manager)
$carnivorousEnclosure = new Enclosure();
$this->addReference('carnivorous-enclosure', $carnivorousEnclosure);
$herbivorousEnclosure = new Enclosure();
$this->addReference('herbivorous-enclosure', $herbivorousEnclosure);
$manager->persist(new Enclosure(true));
$this->addDinosaur($manager, $carnivorousEnclosure, 'Velociraptor', true, 3);
$this->addDinosaur($manager, $carnivorousEnclosure, 'Velociraptor', true, 1);
$this->addDinosaur($manager, $carnivorousEnclosure, 'Velociraptor', true, 5);
$this->addDinosaur($manager, $herbivorousEnclosure, 'Triceratops', false, 7);
... lines 33 - 52

... lines 1 - 10
class LoadSecurityData extends AbstractFixture implements OrderedFixtureInterface
public function load(ObjectManager $manager)
$herbivorousEnclosure = $this->getReference('herbivorous-enclosure');
$this->addSecurity($herbivorousEnclosure, 'Fence', true);
$carnivorousEnclosure = $this->getReference('carnivorous-enclosure');
$this->addSecurity($carnivorousEnclosure, 'Electric fence', false);
$this->addSecurity($carnivorousEnclosure, 'Guard tower', false);
... lines 26 - 39

These two classes build 3 Enclosures and also add some security to them. But, part of this code is using a setEnclosure() method on Dinosaur... and that doesn't exist! Open Dinosaur, scroll to the bottom, and add it: public function setEnclosure() with an Enclosure argument. Set that on the property.

... lines 1 - 10
class Dinosaur
... lines 13 - 81
public function setEnclosure(Enclosure $enclosure)
$this->enclosure = $enclosure;

Awesome! Once the bundle finishes downloading open AppKernel. And inside the if statement, add new DoctrineFixturesBundle(). If you're using Flex, this step will have already been done for you automatically.

... lines 1 - 7
class AppKernel extends Kernel
public function registerBundles()
... lines 12 - 22
if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
... lines 24 - 26
$bundles[] = new DoctrineFixturesBundle();
... lines 28 - 36
... lines 38 - 39
... lines 41 - 60

We haven't hooked the fixtures into our tests yet, but we can at least try them! Run:

php bin/console doctrine:fixtures:load

Go check out the browser! Yes! The fixtures gave us 3 enclosures. That's why I wrote our test to expect 3 rows. If we can load the fixtures when the test runs, we're in business!

Loading Fixtures in the Test

Fortunately, LiipFunctionalTestBundle gives us a really nice way to do this. At the top of the test method, add $this->loadFixtures() and pass an array of the fixture classes you want to load: LoadBasicParkData::class and LoadSecurityData::class.

... lines 1 - 8
class DefaultControllerTest extends WebTestCase
public function testEnclosuresAreShownOnHomepage()
... lines 17 - 25


Since LiipFunctionalTestBundle v3.0 the loadFixtures() method is no longer supported. You should use LiipTestFixturesBundle instead

If you're going to use the same set of fixtures for all your test methods, then moving this to setUp() is a great choice.

Run the tests!

./vendor/bin/phpunit tests/AppBundle/Controller/DefaultControllerTest.php

They work! They pass, over and over again!

So, how would things be different if you did not want to load fixtures? Well, you will still want to empty the database. So, you could use the same trick as the integration tests. Or, you could call $this->loadFixtures() with an empty array.

Of course, the tests fail. That's because loadFixtures() empties the database... but then doesn't load anything into it.

Remember, if you choose this philosophy, you're now responsible for creating the data you need. How? The same way you always do: create some Enclosure objects and then persist them with the EntityManager. And since we're still ultimately extending KernelTestCase, we already know how to get the EntityManager: with self::$kernel->getContainer()->get('doctrine')->getManager().

Test Base Classes

Actually, it would be great if we had a shortcut like $this->getEntityManager() for all our test classes. We won't do it in this tutorial, but I highly recommend creating your own base test class with extra shortcut methods. Typically, I'll have one base test class for integration tests - which extends KernelTestCase - and if necessary, another for my functional tests, which extends WebTestCase. You can also use traits to share code even better.

Faster Database Loading

The LiipFunctionalTestBundle has two other tricks. First, if you're using SQLite, then it automatically builds the schema for you. Check this out: delete the database file:

rm var/data/test.sqlite

Bye bye database schema! But, when you run the tests, they still pass! When you load the fixtures, it creates the schema too. Thanks friends!

The second trick lives in app/config/config_test.yml. Add a new option: cache_sqlite_db set to true.

... lines 1 - 27
cache_sqlite_db: true

Visually... this doesn't make any difference. BUT! Behind the scenes, cool things are happening. Each time you call loadFixtures(), it loads the fixtures and then caches the database file. The next time you call loadFixtures() with the same arguments, it instantly re-uses that cached database file.

Check this out: to simulate loading a lot of fixtures, add a sleep(5) in one of them. Now, run the test:

./vendor/bin/phpunit tests/AppBundle/Controller/DefaultControllerTest.php

Yea... it's slow. The bundle detected the change we made and was smart enough to know that it needed to reload the fixtures. But the second time... zoom! It's super fast.

The coolest part is that all of this database and fixture-handling stuff from LiipFunctionalTestBundle can be used even if you decide to use a different client - like Mink - instead of Symfony's BrowserKit.

Next, let's look at one more trick you can do with fixtures.

Leave a comment!

Login or Register to join the conversation

when I try run this test "./vendor/bin/phpunit tests/AppBundle/Controller/DefaultControllerTest.php", it fails with the following error: "BadMethodCallException: doctrine/doctrine-fixtures-bundle must be installed to use this method."

In composer.json I have these versions:

"doctrine/doctrine-fixtures-bundle": "2.3",
"liip/functional-test-bundle": "~2.0@alpha",
"liip/test-fixtures-bundle": "^1.0.0",
"phpunit/phpunit": "^8.5",
"doctrine/data-fixtures": "1.3",
"doctrine/doctrine-bundle": "^1.6".

And In AppKernel.php I've registered these bundles for 'dev' and 'test' environments:

$bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
$bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
$bundles[] = new DoctrineFixturesBundle();

if ('dev' === $this->getEnvironment()) {
$bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
$bundles[] = new Symfony\Bundle\WebServerBundle\WebServerBundle();

if ('test' === $this->getEnvironment()) {
$bundles[] = new LiipFunctionalTestBundle();

I tried to google for the answer, but didn't find helpful info. Could you help me out to figure out why i get this error, please?

sadikoff Avatar sadikoff | SFCASTS | Mr_T | posted 2 years ago | edited

Hey Mr_T

Try to install exact version of fixtures bundle composer require --dev doctrine/doctrine-fixtures-bundle:2.4.1



hey sadikoff
thanks for your reply. I installed the exact version and now I have "doctrine/doctrine-fixtures-bundle": "2.4.1" in composer.json, but I still get the same exact error as before...


If anyone wonders, I've just solved this issue. What I did was to downgrade "phpunit/phpunit" from "^8.5" to "^6.3" and "liip/functional-test-bundle" from "~2.0@alpha" to "^1.8"


hah you did it faster then I get here =) it was tricky... we already tweaked this course install commands to solve most of issues.

Thanks for sharing solution here!


1 Reply
Thomas V. Avatar
Thomas V. Avatar Thomas V. | posted 4 years ago

Starting from LiipFunctionTestBundle ^3.0.0 loadFixtures is no longer support.
From de Changelog:

Removed fixtures loading in favor of https://github.com/liip/Lii...

Hey Thomas,

Thank you for sharing this with others!


Thomas V. Avatar

My pleasure, just helping people get over the same WTF moment I had :)



When I load fixtures : php bin/console doctrine:fixtures:load, I have an error message :
Class "AppBundle\DataFixtures\ORM\LoadBasicParkData" can't implement "OrderedFixtureInterface" and "DependentFixtureInterface" at the same time.

I use Symfony 3.3.18 and doctrine-fixtures-bundle": "2.4.1"

You know why ?


Hey Stephane

Yep, you can't implement both interfaces :p
If you are on Symfony3 I believe you should only implement "OrderedFixtureInterface"



Hey Diego,
Thank for your reply. But I don't understand why it's not working because I only copy the files about fixtures from tutorial folder. Normaly the code can work with Sf3 ?


Hey Stephane, actually this course is based on Symfony3.3 so it should just work but I just checked the code and we don't implement both interfaces on fixtures classes, somehow you added it. Just stop implementing DependentFixtureInterface and it should work



MolloKhan LoadBasicParkData extends Doctrine\Bundle\FixturesBundle\Fixture which implements DependentFixtureInterface...
When we remove extended class - addReference method is no longer available.
When we remove implementation of OrderedFixtureInterface, well it won't be ordered.
So it's clearly bug in course code.

Probably you should extend Doctrine\Common\DataFixtures\AbstractFixture.


Hey Kuba,

That was totally a bug, not sure how we missed it when was recording the course. Anyway, it's fixed in the source code and code blocks now. And yes, you're right, we need to extend "Doctrine\Common\DataFixtures\AbstractFixture" instead - good guess! So, it's fixed and I just double checked - fixtures are loaded fine now. Literally, changes were: https://github.com/knpunive...

Thank you for reporting this!


1 Reply

Hey kubaf

Wow, you are totally right. I was on a latest release of DoctrineFixturesBundle where Fixture class doesn't implement DependentFixtureInterface anymore. I think your solution is good enough, just extend from Doctrine\Common\DataFixtures\AbstractFixture

We will see what we can do about tutorial's code


Bojan Đ. Avatar
Bojan Đ. Avatar Bojan Đ. | posted 4 years ago


As of this moment, Symfony 3.3.10 is not supported any more by the latest version of doctrine/doctrine-fixtures-bundle. The version 3.0.4 does support Symfony 3.3.10. So to get this to work, `composer require --dev doctrine/doctrine-fixtures-bundle:3.0.4` should be the full command.


Hey Bojan Đ.

Thanks for sharing information about bundle compatibility.

BTW to follow course code it will be better to use fixtures-bundle version 2.4.1.



Hi guys,

there is a typo in the video filename when you download it.



Hey Nicolas!

Thanks for reporting it! Now it's fixed :)


Cat in space

"Houston: no signs of life"
Start the conversation!

While the fundamentals of PHPUnit haven't changed, this tutorial *is* built on an older version of Symfony and PHPUnit.

What PHP libraries does this tutorial use?

// composer.json
    "require": {
        "php": "^7.0, <7.4",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/doctrine-bundle": "^1.6", // 1.10.3
        "doctrine/orm": "^2.5", // v2.7.2
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "sensio/distribution-bundle": "^5.0.19", // v5.0.21
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.28
        "symfony/monolog-bundle": "^3.1.0", // v3.1.2
        "symfony/polyfill-apcu": "^1.0", // v1.6.0
        "symfony/swiftmailer-bundle": "^2.3.10", // v2.6.7
        "symfony/symfony": "3.3.*", // v3.3.13
        "twig/twig": "^1.0||^2.0" // v2.4.4
    "require-dev": {
        "doctrine/data-fixtures": "^1.3", // 1.3.3
        "doctrine/doctrine-fixtures-bundle": "^2.3", // v2.4.1
        "liip/functional-test-bundle": "^1.8", // 1.8.0
        "phpunit/phpunit": "^6.3", // 6.5.2
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0" // v3.4.30