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

Tests, Assertions & Coding

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.

So... we don't really have much code to test yet! That might feel weird: when I started testing, I wanted to write the code first, and then test that it worked.

But actually, you can do it in the opposite order! There's a hipster methodology called Test Driven Development - or TDD - that says you should write the tests first and then your code. We'll talk more about TDD in a few minutes.

Imagining Your Future Code

But yea! That's what we're going to do. We need to use our imagination. Let's imagine that in the Entity directory, we're going to need a Dinosaur class. And each Dinosaur will have a length property, along with getLength() and setLength() methods. So, in DinosaurTest, we might want to test that those methods work correctly.

Actually, testing getter and setter methods is not something I usually do in my code... because they're so simple. But, it's a great place to get started.

Test Directory Structure

Oh, and now it might be a bit more obvious why we called this class DinosaurTest and put it in an AppBundle/Entity directory. As a best-practice, we usually make our tests/ directory match the structure of src/, with one test class per source class. But not all classes will need a test... and there will be some exceptions when we talk about integration and functional tests!

Testing getLength & setLength

Ok, let's test! Create a new public function testSettingLength(). The method needs to start with test, but after that, give it a clever description that will help you recognize what the method is supposed to do.

... lines 1 - 6
class DinosaurTest extends TestCase
{
public function testSettingLength()
{
... line 11
... lines 13 - 17
}
}

Now, even though we don't have a Dinosaur class, we're going to pretend like we do. Ok... $dinosaur = new Dinosaur(). Then, $this->assertSame() that zero is $dinosaur->getLength().

... lines 1 - 6
class DinosaurTest extends TestCase
{
public function testSettingLength()
{
$dinosaur = new Dinosaur();
$this->assertSame(0, $dinosaur->getLength());
... lines 14 - 17
}
}

We <3 Assertions

This tests that - if we don't set a length - it defaults to 0. PHPUnit has a ton of these assert functions. Google for "PHPUnit Assertions" to find an appendix that talks all about them. I'd say there is a plethora of them and you'll learn them as you go, so no need to memorize all of these. There will not be a pop quiz at the end.

For assertSame(), the first argument is the expected value and the second is the actual value. assertSame() is almost the.... same as the most popular assert function: assertEquals(). The only difference is that assertSame() also makes sure the types match.

And honestly, you could just use $this->assertTrue() for everything: 0 === $dinosaur->getLength().

But, using specific assert methods will give you better error messages when things fail.

Coding & Testing

Set the length: $dinosaur->setLength(9). And then assert that it equals 9.

... lines 1 - 6
class DinosaurTest extends TestCase
{
public function testSettingLength()
{
... line 11
... lines 13 - 14
$dinosaur->setLength(9);
... line 16
$this->assertSame(9, $dinosaur->getLength());
}
}

Perfect! Our test is done! I know... kinda strange, right? By writing the test first, it forces us to think about how we want our Dinosaur class to look and act... instead of just diving in and hacking it together.

We know the test will fail, but let's try it anyways! Run:

./vendor/bin/phpunit

Yay!

Adding the Dinosaur Entity

Time to make that test pass! If you downloaded the course code, then you should have a tutorial/ directory with a Dinosaur class inside. Copy that and paste it into the real Entity directory.

... lines 1 - 2
namespace AppBundle\Entity;
... line 4
use Doctrine\ORM\Mapping as ORM;
... line 6
/**
* @ORM\Entity
* @ORM\Table(name="dinosaurs")
*/
class Dinosaur
{
/**
* @ORM\Column(type="integer")
*/
private $length = 0;
... lines 17 - 26
}

This is just a simple class with a length property. It does have Doctrine annotations, but that's not important! Sure, we will eventually be able to save dinosaurs to the database, but our test doesn't care about that: it just checks to make sure setting and getting the length works.

Let's add those methods: public function getLength() that returns an int. And public function setLength() with an int argument. Set the length property.

... lines 1 - 10
class Dinosaur
{
... lines 13 - 17
public function getLength(): int
{
return $this->length;
}
public function setLength(int $length)
{
$this->length = $length;
}
}

Back in DinosaurTest, add the use statement. Ah, PhpStorm is as happy as a raptor in a kitchen!

... lines 1 - 2
namespace Tests\AppBundle\Entity;
... line 4
use AppBundle\Entity\Dinosaur;
... lines 6 - 7
class DinosaurTest extends TestCase
... lines 9 - 20

Ok, find your terminal and... test!

./vendor/bin/phpunit

Yes! Celebration time! This is our very first - of many passing tests!

Testing for Bugs

Let's add one more quickly: imagine that a bug has been reported! Gasp! People are saying that if they create a Dinosaur of length 15, by the time it is born and grows up, it's smaller than 15! The dinos are shrinking! Probably a good thing.

Let's add a test for this: public function testDinosaurHasNotShrunk. Start the same as before: $dinosaur = new Dinosaur(), and $dinosaur->setLength(15).

... lines 1 - 7
class DinosaurTest extends TestCase
{
... lines 10 - 20
public function testDinosaurHasNotShrunk()
{
$dinosaur = new Dinosaur();
... line 24
$dinosaur->setLength(15);
... lines 26 - 27
}
}

And just to make things more interesting, imagine that it's OK if the dinosaur shrinks a little bit... it just can't shrink too much. The guests want a thrill! In other words, $this->assertGreatherThan(12, $dinosaur->getLength()).

... lines 1 - 7
class DinosaurTest extends TestCase
{
... lines 10 - 20
public function testDinosaurHasNotShrunk()
{
... lines 23 - 26
$this->assertGreaterThan(12, $dinosaur->getLength(), 'Did you put it in the washing machine?');
}
}

You can also add an optional message as the last argument to any assert function. This will display when the test fails... which can sometimes make debugging easier.

Ok, try the test!

./vendor/bin/phpunit

Because our code is actually perfect, it passes! But if you make it fail temporarily and run the test again... there's our message, along with the normal failed assertion text.

Hey! In just a few minutes, we wrote our first test and even used test-driven development! It's time to learn more about that... and all the different types of tests you can write.

Leave a comment!

22
Login or Register to join the conversation
CloudCreators Avatar
CloudCreators Avatar CloudCreators | posted 2 years ago

(base) shubham@Shubhams-MacBook-Air phpUnit % ./vendor/bin/simple-phpunit
PHPUnit 8.5.19 by Sebastian Bergmann and contributors.

No tests executed!
(base) shubham@Shubhams-MacBook-Air phpUnit %

I wrote the same tests but the tests are not getting executed idk why?

Reply

Hey Shubham,

Please, double check your PHPUnit config file that should be in your project root folder called phpunit.xml or phpunit.xml.dist. There you specify where your tests live and what to execute and how, see related video https://symfonycasts.com/sc...

Or, you can specify what to execute in the command, e.g:

$ ./vendor/bin/simple-phpunit path/to/your/test/file.php

Also, double-check that you extend the correct file in your test file, it should be "\PHPUnit\Framework\TestCase" class, but may depends on your PHPUnit version.

Cheers!

Reply
Peter Z. Avatar
Peter Z. Avatar Peter Z. | posted 2 years ago

Hi , I am PHP fresher and i would like to study on this course. But i am not able to download the course code to start.
Does anyone have same issue or know the solution to fix it?
BTW, i did buy the access.

Thank you very much!

Reply

Hey Peter,

We're sorry about that. Hm, I just double-checked and course code download works for me on this course. Are you sure you're logged in in your account that has active subscription? Do you see the "Course code" item when you press "Download" button in the top right corner? Is it active, like can you press on it? Any errors? Probably your browser's security does not allow to download the file, please look closer on any notifications popping up in your browser - you may need to allow it manually. Or, as an alternative solution, try to download course code in a different browser. Does it help!

Please, let us know if you still have this problem!

Cheers!

Reply

Hello,

when testing the testThatYourComputerWorks() function everything is OK, but I get an error when testing the testSettingLength() function.

I get this message:

PHP Fatal error: Uncaught Error: Class 'PHPUnit\Framework\ExceptionWrapper' not found in /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestResult.php:745

can you please help me?

thank you

Reply
Default user avatar

I also passing this tutorial on Vagrant with Ubuntu in a box. My working version of simple LAMP (with PHP 7.2 on ubuntu/xenial64):
https://bitbucket.org/renta...
https://bitbucket.org/renta...

1 Reply

Hey chieroz

Can you tell me how are you executing your tests (which command are you running)?
And, which versions of PHP and PHPUnit are you using?

Cheers!

Reply

Hello Diego,

I am using a Vagrant VM with Ubuntu 16.04 on a host with OSX 10.13.1

in the guest machine I have PHP 7.1, PHPUnit 6.4.4

I am just following the tutorial, so I execute phpunit with: ./vendor/bin/phpunit

this is the full error:

vagrant@vagrant:/var/www/knp.phpunit$ ./vendor/bin/phpunit
PHPUnit 6.4.4 by Sebastian Bergmann and contributors.

FPHP Fatal error: Uncaught Error: Class 'PHPUnit\Framework\ExceptionWrapper' not found in /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestResult.php:745
Stack trace:
#0 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestCase.php(894): PHPUnit\Framework\TestResult->run(Object(Tests\AppBundle\Entity\DinosaurTest))
#1 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestSuite.php(744): PHPUnit\Framework\TestCase->run(Object(PHPUnit\Framework\TestResult))
#2 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestSuite.php(744): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#3 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/TextUI/TestRunner.php(537): PHPUnit\Framework\TestSuite->run(Object(PHPUnit\Framework\TestResult))
#4 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/TextUI/Command.php(195): PHPUnit\TextUI\TestRunner->doRun(Object(PHPUnit\Framework\TestSuite), Array, true)
#5 /var/www/knp.phpunit/vendor/phpunit/phpunit/src/TextUI/Command.php(148): PHPUnit\TextUI\ in /var/www/knp.phpunit/vendor/phpunit/phpunit/src/Framework/TestResult.php on line 745

Thank you for your attention.

Carlo

Reply

Hmm, interesting... I would like to see your phpunit.xml and composer.json files too (maybe you can upload them to the cloud)
BTW, try extending from this class instead `\PHPUnit_Framework_TestCase`, I believe it will work, but it should work with the other class as well

-1 Reply

Hmm, your files look good to me. Try running "composer update" and double check your namespaces
Did extending from `\PHPUnit_Framework_TestCase` class work?

Reply

Well, this is REALLY weird.

extending from `\PHPUnit_Framework_TestCase` class did not work

after that, I tried with "composer update" and after that I did not get the error message but a quite creepy "You need to set up the project dependencies using Composer: composer install" message

Obviously enough, I run "composer install" and nothing happened but I realized that the symlink ./vendor/bin/phpunit got broken. I deleted everything in the ./vendor folder. again, I get the Class 'PHPUnit\Framework\ExceptionWrapper' not found message.

I even tried to destroy and rebuild the VM with no success. maybe I can try with a different distro, but it seems a little paranoid.

next step: banging my head against the wall... ;-)

thank you for your help

carlo

Reply

hahaha, before smashing your head against a wall, let's try something else.
Change your PHPUnit version to "^6.3" instead of 6.4, maybe there is something in that version that is causing this weird behaviour

Reply

BINGO!

with phpunit 6.3.1 everything works.

I built another VM, this time with CentOS 7 and php 7.1: in this environment PHPUnit 6.4 works perfectly

thank you for your help. have a nice day

Reply

Yes!! I'm so glad to hear you could fix it, and this is very interesting, because PHPUnit 6.4 version was in someway incompatible with your old VM setup

Cheers!

Reply

I think Ubuntu is somewhat incompatible because I built a fresh VM with no success while CentOS 7 + PHP7.1 + PHPUnit 6.4.* is OK

cheers!!!

4 Reply
Default user avatar
Default user avatar Yahya A. Erturan | chieroz | posted 5 years ago | edited

chieroz if it is a development environment I strongly advise you to switch Docker. It took me a lot of pain and time to switch it but I finally automated a docker for development. Check it out. https://github.com/yahyaert...

1 Reply

For some reason I don't have an auto completion for "assertTrue" method. But after I wrote it manually, PhpStorm identified the method properly. Do you have any ideas of why it might be?

Reply

Yo boykodev!

Hmm. I don't know :/. It's possible that PhpStorm sees 2 copies of PHPUnit in your project for some reason - it's the only thing I can think of. If you hold Ctrl (or Cmd on a mac) and click the TestCase class, does it open that file immediately? If so... then... my theory is wrong and I don't know! But if it asks you to choose between 2 different files, then that's it! And in that case, on copy should probably be ignored (not sure why there would be 2 anyways)

Cheers!

Reply

It goes straight to the class. However, autocompletion does work after pressing (Ctrl + Space), I think it's called "explicitly invoked completion".

Reply
Default user avatar
Default user avatar Mykel Chang | posted 5 years ago

Hello,

I didn't know where to mention it so :

- there's a little error, if you search for "$dinosuar", you'll find it :)
- there's an "i" in capital letter surrounded by an tag which makes it look like a "/" -> I found it disturbing

Reply

Hey Mykel Chang!

This is a *great* place to mention it :). I've just made the fixes - https://github.com/knpunive... - thanks!

And that was fast! We haven't technically even released this chapter yet ;).

Cheers!

1 Reply
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
    }
}
userVoice