PHPUnit: Secure the Park

Let me paint you a scary picture:

It's Friday night. It's stormy. The office is empty and you're deploying fresh code straight to production. Suddenly, an alarm! What? The fences are down!? The dinosaurs are escaping!? Somehow, your beautiful code contained a bug. And as the raptors surround you, one thought keeps coming back: if only you had written tests.

I hate bugs, I hate fixing emergencies on production, and I especially hate being eaten by raptors.

It's time to go pro with coding... and that means, learning to test! And not just because we hate running from dinosaurs. We want to write code that is thoughtfully-designed and have the ability to add new features with confidence.

Hello Dinosaur Park

To make this dream come alive, you should totally code along with me. Download the course code from this page. After unzipping the file and turning the fences back on, you should find a start/ directory that has the same code you see here. Open up the file for setup instructions and directions on how to catch the last boat off the island before the storm.

The last step is to find a terminal, move into your project, and run:

./bin/console server:run

to start the built-in PHP web server. In your browser, go to http://localhost:8000 to find... well... nothing! Just, "Welcome to Dinosaur Park". Instead of creating a park full of dinosaurs and then worrying about security... ahem... we don't have any code yet. We're going to build this dino factory and write tests all at the same time.

Installing PHPUnit

The de facto standard tool for testing in PHP is PHPUnit. Open a new terminal tab. Install it with:

composer require --dev phpunit/phpunit

This will obviously download the PHPUnit library into your vendor/ directory. But mostly, you will interact with PHPUnit as an executable. When this finishes, you can now run:


Hi PHPUnit! And hello Sebastian Bergmann and other contributors! There are no tests yet but I already feel like we're making friends.

Write some Tests

Let's write our first test. But, uh, don't worry about what we're testing yet - let's just experiment a little.

Create a tests/ directory and inside, another AppBundle/Entity directory. We'll talk about this structure soon, but first we have dinosaurs to contain!

Add a new PHP class: DinosaurTest, and give it a namespace: Tests\AppBundle\Entity. Make sure you extend a class: TestCase from PHPUnit.

... lines 1 - 2
namespace Tests\AppBundle\Entity;
... line 4
use PHPUnit\Framework\TestCase;
... line 6
class DinosaurTest extends TestCase
... lines 9 - 12

To actually make a test, create a public function called testThatYourComputerWorks. We're giving it that name because, inside, we're going to say $this->assertTrue(false).

... lines 1 - 6
class DinosaurTest extends TestCase
public function testThatYourComputerWorks()

If this test passes, you'll know to throw your computer out of the window and buy a new one. Let's find out. To run the tests, find your terminal, and re-run PHPUnit:


Yes! It fails! My computer gets to live! It failed asserting that false is true on DinosaurTest line 11.

Test Rules & Best Practices

Ok, let's talk about the basic rules of writing a PHPUnit test. First, you can technically put your test classes anywhere... but.. you've gotta admit that tests/ is a pretty good place. Actually, in a Symfony project, you automatically start with a phpunit.xml.dist file. Well, in Symfony 4, this is added when you install phpunit. We'll talk more about this file later... but PHPUnit reads this automatically. And... check it out! It says that our tests all live in... tests/. That's how PHPUnit is able to find our DinosaurTest.

Second, our test has a namespace... but that's not really important. In a Symfony project, your composer.json file has an autoload-dev section that basically says that anything in tests/ should start with the namespace Tests. No big deal.

Let's get to the really important stuff, because PHPUnit does have a few crucial rules. First, your test class must extend TestCase and end in the word Test. Second, all of your test methods must be public and start with the word test. When you run PHPUnit, it basically looks for all classes ending in Test and all public functions inside starting with test.

Got it? Good... because the storm is coming, and we've got work to do. Delete the fake test. Let's start coding.

Leave a comment!

  • 2019-04-11 Diego Aguiar

    Hey Daniel Gustaw

    If you are on Symfony, then yes, that's the prefered way for working with PHPUnit (by installing symfony/phpunit-bridge). We didn't mention that because this course is not really focused on Symfony but sorry anyways for the confusion.

    If you want to read more about phpunit-bridge give it a check to the docs:


  • 2019-04-09 Daniel Gustaw

    After: composer require --dev phpunit/phpunit I have:


    Adding phpunit/phpunit as a dependency is discouraged in favor of Symfony's PHPUnit Bridge.

    * Instead:
    1. Remove it now: composer remove --dev phpunit/phpunit
    2. Use Symfony's bridge: composer require --dev phpunit


    Please update.

  • 2019-02-01 weaverryan

    Hey Carlos Eduardo!

    You're using symfony/phpunit-bridge, which indeed does things a bit differently :). Instead of downloading phpunit into your vendor directory (and all its dependencies), it creates a separate composer.json file and downloads phpunit and all its dependencies into its own isolated directory. The short explanation for why this is, is that it avoids dependency issues between the version of libraries that your app requires and the version that PHPUnit requires. And you were already looking in the right place, there's a script that literally downloads PHPUnit and its dependencies into, in your example, bin/.phpunit/phpunit-6.5. That vendor/ directory is autoloaded, as well as your normal vendor dir. It's definitely some magic, but the point is to work around a few version dependency issues.


  • 2019-01-31 Carlos Eduardo

    But Diego, it isnt there. The only required package is the "symfony/phpunit-bridge": "^4.2". And in the composer.json of phpunit-bridge we see:

    "conflict": {
    "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0"

    My vendor folder only contains this:


    I think that the "magic" is done with the phpunit inside the bin folder: ./bin/.phpunit/phpunit-6.5. But I can't figure it out how the autoload finds the classes inside this folder.


  • 2019-01-31 Diego Aguiar

    Hey Carlos Eduardo

    It's not magic, you should find that file inside vendor/phpunit/phpunit/src/Framework/TestCase.php

    Probably the file location depends on your PHPUnit version but if you are on version 7 (or maybe even version 6) you should find it right there.


  • 2019-01-31 Carlos Eduardo

    Hey!! What's the magic with "use PHPUnit\Framework\TestCase;" ?? Because I can find it anywhere, and even PHPStorm doesnt. But the tests work with vendor/bin/simple-phpunit.

  • 2018-09-11 Nicolás González Flores

    Thanks a lot weaverryan !

    I'm going to finish the course in Symfony 3, to get used to testing, then I'll do it again in Symfony 4 taking in account your notes.

    I'll leave comments / questions in those videos where I get stuck or find big differences.


  • 2018-09-10 weaverryan

    Hey Nicolás González Flores!

    Great question! A few things:

    1) Because much of this tutorial is strictly about unit testing, this is exactly the same in Symfony 4 (because unit tests are totally isolated from your framework). So, that's excellent :).

    2) The directory structure in this project versus a Symfony 4 project is slightly different. But, again, because we're mostly unit testing, the changes are superficial - e.g. `tests/AppBundle/Entity` in this tutorial vs just tests/Entity in Symfony 4.

    3) When it comes to "integration" or "functional" tests - the tests that DO interact with the framework - there *are* some differences. Most notably, there is no config_test.yml file where you can configure the database credentials in the test environment like we did - Instead, you're supposed to copy phpunit.xml.dist to phpunit.xml, and customize the environment variables in that file for what you want in the test environment. Actually, this is the BIG difference overall. In Symfony 4, you have the .env file with environment variables. But, in the test environment, the environment variables in phpunit.xml(.dist) are used instead. So, you have them duplicated. It's kind of a pain - and something I'm working on fixing actually. But anyways, the point is that the way you configure your Symfony app to behave differently in the test environment only, is a bit different. If you get stuck on a detail like that, definitely ask us down in the comments :).

    I hope you enjoy it! This was a fun one to make!


  • 2018-09-09 Nicolás González Flores


    I'm excited to start this tutorial. I've been following the Symfony 4 courses and I see this tutorial is made using symfony 3. What are the differences I should consider if I wanna use sf 4 instead of 3?


  • 2018-05-17 Victor Bocharsky

    Hey Coder,

    Haha, good detective work! :) Actually, Leanna's profile looks a bit outdated, because now she's a real programmer, and she does write PHP code as well lately. But because she's also a head of creativity here at KnpUniversity - we just do not allow her coding PHP too much :p Anyway, thanks for your feedback!


  • 2018-05-13 Coder

    Loving your voice :) but on your linked in side I see you are not a programmer, besides html and css. So it looks interesting, look like you know what you are doing in the videos, not just blindly reading script :)

  • 2018-04-25 Chris

    I enjoy Leanna's videos, though. It's a nice mixture, at Knpuniversity. But this shouldn't become a discussion thread here :-)

  • 2017-11-21 Serge Boyko

    For those who don't remember:

  • 2017-11-21 Serge Boyko

    Loved the "Jurassic Park" reference!
    - It's a Unix system!

  • 2017-11-07 weaverryan

    Hey there!

    Ah, thanks for the feedback! Leanna *is* actually a developer, though you're correct that she isn't the person who originally wrote these tutorials. And the jokes are actually a mixture of jokes she wrote, that I wrote and also some that Andrew wrote (he's awesome at this!). So, I know it's a different voice than on most of the tutorials, but I love having her variety and personality mixed in on some of our courses :). But, we *are* going to look into the "s" sound & microphone placement to see if we can improve on that.


  • 2017-11-07 ann.

    Sorry guys for these critics :(
    My co-workers and me have the same problem.
    It is big difference between Ryan tutorials( maybe because he is a developer ) and voice of this girl - totally unnatural ( because she is not a developer).
    When she speaks I always hear this "word ssssss, word ssssssss, word sssssss", maybe she is very close to microfone.
    I checked old tutorials and the same issue. The same girl again in action :)
    If you compare one of Ryan tutorial and this one, it is a huge difference.
    Somebody (developer) wrote text and she tries to read, her jokes without emotions are totally unnatural.

  • 2017-11-07 weaverryan

    <3 this so much! What a great example of a way that tests help you that you don't realize until you really dive into them.

  • 2017-11-07 Milan Vlach

    Another good example why creating tests during your development process (quite a different one than getting eaten by a Dino :)) is when you are developing a new technology you are not familiar with, and instead of writing a testing environemt (a class or function that does everything you need), you can write the UnitTests or the FunctionalTests which would test each and every aspect of your new logic.

    What is so good about this approach?
    - Well, it helps you think on the most elementary level - what your logic REALLY needs and not getting tangled inside a labyrinth.

    All of this approach expects that you write tests before you write your logic. Now, I myself consider more comfortable to image and think about every aspect of my application before I start coding it and consider it good practise - It's up to you (YOU THE READER) which approach is more comfortable for you.
    You hold the reins of your universe in your own hands, mate :)

  • 2017-11-07 Jelle Schouwstra

    Oh I am SO looking forward to this!