This tutorial has a new version, check it out!

Controlling Data / Fixtures in a Test

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 $12.00

Controlling Data / Fixtures in a Test

When we try running the test again, it fails! What the heck?

If we did a little debugging we’d see that it fails the second time because the username and email in the test are already taken. So instead of success, we see a validation error.

This is a very important detail about testing. Before running any test, we need to make sure the database is in a predictable state. Our test will pass... unless there happens to already be a user5 in the database.

Deleting Users

To fix this, let’s empty the user table before running the test. Start by grabbing the container object, which is stored statically on the parent test class:

// src/Yoda/UserBundle/Tests/Controller/RegisterControllerTest.php
// ...

public function testRegister()
    $client = static::createClient();

    $container = self::$kernel->getContainer();
    // ...

Now, grab the Doctrine entity manager by getting the doctrine service and calling getManager. If you’re not comfortable with what I just did, don’t worry. I’ll demystify this in a moment:

// src/Yoda/UserBundle/Tests/Controller/RegisterControllerTest.php
// ...

public function testRegister()
    $client = static::createClient();

    $container = self::$kernel->getContainer();
    $em = $container->get('doctrine')->getManager();

    // ...

Once we have the entity manager we can get the UserRepository. Build a query from the repository that deletes all of the users:

// src/Yoda/UserBundle/Tests/Controller/RegisterControllerTest.php
// ...

public function testRegister()
    // ...
    $em = $container->get('doctrine')->getManager();
    $userRepo = $em->getRepository('UserBundle:User');

    // ... the actual test

Re-run the test again to see that everything passes. Now this is a good-looking test!

Notice that we’re not testing for every tiny detail. That would be a ton of work and functional tests aren’t meant to replace us as developers from making sure things work when we build them.

Instead, we just want to see that the form can be submitted successfully and unsuccessfully. The important thing is that if we break something major later, our test will let us know.

Separating the dev and test Databases

When we run our tests, it’s emptying the same user table we’re using for development! That’s kind of annoying! Instead, let’s use 2 different databases: one for development and one that’s used only by the tests.

Open up the main config.yml file and find the doctrine database configuration. Copy and paste it into the config_test.yml file, but remove everything except for the dbname option. Now, add an _test to the end of it:

# app/config/config_test.yml
# ...

        dbname:   %database_name%_test


The database_name is a parameter, which lives in app/config/parameters.yml.

This little trick takes advantage of how Symfony environments work. The test environment uses all of the normal Doctrine configuration, except that it overrides this one option.

Don’t forget to setup the new database by running doctrine:database:create. Pass an extra --env=test option so that the command runs in the test environment. Use the same idea to insert the schema:

php app/console doctrine:database:create --env=test
php app/console doctrine:schema:create --env=test


By default, all app/console commands run in the dev environment.

You can now re-run the tests knowing that our main database isn’t being affected:

php bin/phpunit -c app


As cool as this is, in reality we use a tool called Behat instead of Symfony’s built in functional testing tools. And you’re in luck because everything you just learned translates to Behat. Check out our tutorial on this to take your functional testing into space!

Do this or risk an angry phone call from Darth Vader when the super laser doesn’t fire because you added a new espresso machine to the breakroom.

Leave a comment!

  • 2019-03-29 Victor Bocharsky

    Hey Beniamin!

    What is your question here? It's not clear enough what you are trying to say us in the comment.


  • 2019-03-28 Beniamin Tolan

    Feature: Programmer
    In order to battle projects
    As an API client
    I need to be able to create programmers and power them up

    Background: # features\api\programmer.feature:6
    Given the user "weaverryan" exists # ProjectContext::theUserExists()

    Scenario: Create a programmer # features\api\programmer.feature:9
    Given I have the payload: # ApiFeatureContext::iHaveThePayload()
    "nickname" : "ObjectOrienter",
    "avatarNumber" : 12,
    "tagLine" : "I'm from a test!"
    When I request "POST /api/programmers" # ApiFeatureContext::iRequest()
    Then the response status code should be 201 # ApiFeatureContext::iGetAResponse()
    And the "Location" header should be "/api/programmers/ObjectOrienter" # ApiFeatureContext::theHeaderShouldBe()
    And the "nickname" property should equal "ObjectOrienter" # ApiFeatureContext::thePropertyEquals()

    Scenario: GET one programmer # features\api\programmer.feature:23
    Given the following programmers exist: # ProjectContext::theFollowingProgrammersExist()
    | nickname | avatarNumber |
    | UnitTester | 3 |
    When I request "GET /api/programmers/UnitTester" # ApiFeatureContext::iRequest()
    Then the response status code should be 200 # ApiFeatureContext::iGetAResponse()
    Output is "text/html; charset=UTF-8", which is not JSON and is therefore scary. Run the request manually.
    Failed asserting that 404 is identical to 200.
    And the following properties should exist: # ApiFeatureContext::thePropertiesExist()
    And the "nickname" property should equal "UnitTester" # ApiFeatureContext::thePropertyEquals()

    Failure! when making the following request:
    GET: http://localhost:8000/api/programmers/UnitTester

    Failure! Below is a summary of the HTML response from the server.
    Sorry, the page you are looking for could not be found.

    NotFoundHttpException in BaseController.php line 171:
    Oh no! This programmer has deserted. we'll send a serch patrol to search him

    2 scenarios (1 passed, 1 failed)
    12 steps (9 passed, 2 skipped, 1 failed)

  • 2017-10-09 Victor Bocharsky

    Hey argy_13 ,

    Ah, a silly misprint and then its copy/paste in a few more places. Thanks for let us know! I fixed it in:


  • 2017-10-08 argy_13

    Also a few lines later the same command

  • 2017-10-08 argy_13

    Hello guys! just to mention something
    Your first phrase when you have written the video speech, under the video is:

    Let’s run our test a second time:

    $ php vendir/bin/behat

    but of course the command to the terminal is

    $ php vendor/bin/behat


  • 2016-08-29 weaverryan

    That's right Tael! Just surround any % characters with quotes and you'll be just fine now and going forward :)

  • 2016-08-29 Tael Kim

    NOTE THAT: yml config syntax will be changed.
    Not quoting a scalar starting with the "%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0

  • 2015-12-15 weaverryan

    Hi Sarang!

    Ah, this is actually pretty cool! When something changes in Symfony (e.g. from version 2.7 to 2.8), instead of just changing it and breaking your code, Symfony deprecates the old functionality. AND, it also tells you about any deprecated code paths when you run your tests so you know about them and can upgrade :). Some things that we show in this tutorial are deprecated now (we're working on new versions currently), and this is a great example. In RegisterFormType, you should rename setDefaultOptions to configureOptions - here's an example (look at the first 2 code blocks):

    When you fix the deprecation, your tests will pass :). This is actually one of the *killer* features of Symfony. If you want to learn more about it, this is a great presentation about it:


  • 2015-12-15 Sarang Bondre

    Am getting this error


    Failed asserting that false is true.


    Tests: 3, Assertions: 7, Failures: 1.

    Remaining deprecation notices (2)

    UserBundle\Form\RegisterFormType: The FormTypeInterface::setDefaultOptions() method is deprecated since version 2.7 and will be removed in 3.0. Use configureOptions() instead. This method will be added to the FormTypeInterface with Symfony 3.0: 2x

    2x in RegisterControllerTest::testRegister from UserBundle\Tests\Controller

  • 2015-11-23 docLommy

    Even GREATER answer. Smooth and elegant solution - I'll try that. Thnxbye :)

  • 2015-11-20 weaverryan

    GREAT question. Unless you're using the "symfony2" driver via the Symfony2Extension (which I don't use), Behat is making *real* HTTP requests. So, you'll need to create a new app_test.php file that boots Symfony in the "test" environment. Then, go into your config_test.yml file and override doctrine's "dbname" key to be a different database name. Finally, update behat.yml to use this app_test.php in your base URL - like localhost:8000/app_test.php

    Does that make sense?

  • 2015-11-20 docLommy

    Hiya ! How can I target my test database via behat ? Is there a specific behat config file for this ? Or do I reference a specific test environment when running behat ?