PHPUnit: Secure the Park

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

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 README.md 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:

./vendor/bin/phpunit

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()
{
$this->assertTrue(false);
}
}

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:

./vendor/bin/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!

  • 2020-05-12 weaverryan

    Hey Liviu Matei!

    That is 100% our bad! We recently upgraded dependencies on some tutorial to make sure that they *could* work on php 7.4. For a small number of tutorials, we were missing some config, and we accidentally upgraded the dependencies so that they *required* PHP 7.4. We're going to fix that :p. At the moment, you should be able run composer update to fix this - but we're going to fix the code download ASAP.

    Thanks for the comment!

  • 2020-05-09 Liviu Matei

    I'm having some issues with composer install. the composer lock file from the downloaded code sais i need ocramius 1.7 which requires php ^7.4... what gives?

  • 2020-04-20 Vladimir Sadicov

    Hey Julien Bonnier

    We are very sorry that you got stuck with configurations and spent a lot of time to get all working. We are now developing a feature that will show package versions for each course and some requirements but it will be great to hear what issues have you faced? You mentioned that problem was in PHP version and package requirements can you describe some of them? Because our courses doesn't have strict requirements about PHP and it's packages.

    Cheers!

  • 2020-04-18 Julien Bonnier

    Would be very nice to include what PHP version (and when applicable what node version) you are using on a project for all the courses. I don't have PHP installed on my pc so I'm using Docker, I had to rebuild my entire container 4 times before getting a PHP version that match all packages requirements.

  • 2020-03-09 weaverryan

    Hey Amit V Khajanchi!

    Thanks for the note! I know this error - it IS indeed a problem with an older version of doctrine/annotations. We'll see if we can upgrade it in the course code :).

    Cheers!

  • 2020-03-06 Amit V Khajanchi

    Hi - I just started watching the tutorial and was trying to setup the project. I was getting error saying that annotation were not enabled and null is being passed from routing.yml. This project is based on Symfony 3.4... but I had php 7.4 on my machine. Once I downgraded to php 7.2 it started working and now I can follow the tutorial. In case anyone is trying to follow the tutorial and get weird error on annotation like I did, try down grading to php 7.2.

  • 2020-02-10 Victor Bocharsky

    Hey Alex,

    Well done! Glad you solved this issue yourself! :) Actually, Composer docs have a few more solutions for this, check it out: https://getcomposer.org/doc... - but basically, you did it correct!

    Cheers!

  • 2020-02-10 Alex

    After long digging in google, I found a solution. Maybe this will help someone. The right command to execute for my system is "php -d memory_limit=-1 /usr/local/Cellar/composer/1.9.2/bin/composer require --dev phpunit/phpunit:^7

  • 2020-02-10 Alex

    Hello,
    I'm using code from "Download" button of this tutorial. When I try to execute command "composer require --dev phpunit/phpunit" I get "Updating dependencies (including require-dev)
    PHP Fatal error: Allowed memory size of 1610612736 bytes exhausted (tried to allocate 4096 bytes) in phar:///usr/local/Cellar/composer/1.9.2/bin/composer/src/Composer/DependencyResolver/Solver.php on line 223

    So I can't install phpunit. I have only 16Gb RAM. Explain please, how can I install phpunit in your code. My php version 7.2 Thanx in advance.

  • 2019-07-16 Руслан Крупенько

    It helped, thank you!

  • 2019-07-15 Diego Aguiar

    Hey man!

    That's a problem related to PHP 7.3, try downgrading to 7.2 or upgrade your composer dependencies by running
    `composer update

    Cheers!

  • 2019-07-13 Руслан Крупенько

    Hello!
    I can't to install dependences properly. I have an error in the end of command "composer install"
    [Symfony\Component\Debug\Exception\ContextErrorException]
    Warning: "continue" targeting switch is equivalent to "break". Did you mean
    to use "continue 2"?


    Script Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::clearCache handling the symfony-scripts event terminated with an exception


    [RuntimeException]
    An error occurred when executing the "'cache:clear --no-warmup'" command:





    [Symfony\Component\Debug\Exception\ContextErrorException]
    Warning: "continue" targeting switch is equivalent to "break". Did you mean
    to use "continue 2"?

    Also I have the same error when I try to create a database with doctrine. Please help, how can I fix it?

  • 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: https://symfony.com/doc/cur...

    Cheers!

  • 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.

    Cheers!

  • 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:

    autoload.php
    /bin
    /composer
    /guzzlehttp
    /psr
    /ralouphie
    /symfony

    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.

    Thanks

  • 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.

    Cheers!

  • 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.

    Best!

  • 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 - https://knpuniversity.com/s.... 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!

    Cheers!

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

    Hello!

    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?

    Thanks!

  • 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!

    Cheers!

  • 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:
    https://www.youtube.com/wat...

  • 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.

    Cheers!

  • 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!