JSON Test Assertions & Seeding the Database
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.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeLet's make this test real with data and assertions.
There are two main ways to do assertions with Browser. First, it comes with a bunch of built-in methods to help, like ->assertJson(). Or... you can always just grab the JSON that comes back from an endpoint and check things using the built-in PHPUnit assertions you know and love. We'll see both.
Let's start by checking ->assertJson():
| // ... lines 1 - 8 | |
| class DragonTreasureResourceTest extends KernelTestCase | |
| { | |
| // ... lines 11 - 13 | |
| public function testGetCollectionOfTreasures(): void | |
| { | |
| $this->browser() | |
| ->get('/api/treasures') | |
| // ... line 18 | |
| ->assertJson() | |
| ; | |
| } | |
| } |
When we run that:
symfony php bin/phpunit
It passes! Cool! We know that this response should have a hydra:totalItems property set to the number of results. Right now, our database is empty... but we can at least assert that it matches zero.
To do that, use ->assertJsonMatches().
This is a special method from Browser that uses a special syntax that allows us to read different parts off the JSON. We'll dig into it in a minute.
But this one is simple: assert that hydra:totalItems equals 0:
| // ... lines 1 - 8 | |
| class DragonTreasureResourceTest extends KernelTestCase | |
| { | |
| // ... lines 11 - 13 | |
| public function testGetCollectionOfTreasures(): void | |
| { | |
| $this->browser() | |
| // ... lines 17 - 18 | |
| ->assertJson() | |
| ->assertJsonMatches('hydra:totalItems', 0) | |
| ; | |
| } | |
| } |
When we try this:
symfony php bin/phpunit
It fails! But with a great error:
mtdowling/jmespath.phpis required to search JSON
Hello JMESPath
Ah, we need to install that! Copy the composer require line, find your terminal, and run it:
composer require mtdowling/jmespath.php --dev
This "JMESPath" thing is actually super cool: it's a "query language" for reading different parts of any JSON. For example, if this is your JSON and you want to read the a key, just say a. Simple.
But you can also do deeper, like: a.b.c.d. Or, get crazier: grab the 1 index, or grab a.b.c, then the 0 index, .d, the 1 index then the 0 index. You can even slice the array in different ways. Basically... you can go nuts.
But we're not going to lose our minds with this. It's a handy syntax... but if things get too complex, we can always test the JSON manually, which we'll do in a bit.
Anyway, now that we have the library installed, let's run the test again.
symfony php bin/phpunit
It still fails! With a weird error:
Syntax error at character 5
hydra:totalItems.
Unfortunately, the : is a special character inside of JMESPath. So whenever we have a :, we need to put quotes around that key:
| // ... lines 1 - 8 | |
| class DragonTreasureResourceTest extends KernelTestCase | |
| { | |
| // ... lines 11 - 13 | |
| public function testGetCollectionOfTreasures(): void | |
| { | |
| $this->browser() | |
| // ... lines 17 - 19 | |
| ->assertJsonMatches('"hydra:totalItems"', 0) | |
| ; | |
| } | |
| } |
Not ideal, but not a huge inconvenience.
Now when we try it:
symfony php bin/phpunit
It passes!
Seeding the Database
But... this isn't a very interesting test: we're just asserting that we get nothing back... because the database is empty. To make our test real, we need data: we need to seed the database with data at the start of the test.
Tip
To use Foundry factories in a test, also add a use Factories; trait to the top of your test class.
Things worked without that in this case, but in the future, you'll likely get an error.
Fortunately, Foundry makes that dead-simple. At the top, call DragonTreasureFactory::createMany() and let's create 5 treasures. Now, below, assert that we get 5 results:
| // ... lines 1 - 4 | |
| use App\Factory\DragonTreasureFactory; | |
| // ... lines 6 - 9 | |
| class DragonTreasureResourceTest extends KernelTestCase | |
| { | |
| // ... lines 12 - 14 | |
| public function testGetCollectionOfTreasures(): void | |
| { | |
| DragonTreasureFactory::createMany(5); | |
| $this->browser() | |
| // ... lines 20 - 22 | |
| ->assertJsonMatches('"hydra:totalItems"', 5) | |
| // ... line 24 | |
| ; | |
| } | |
| } |
It's just that simple. And actually, let me put our dump back so we can see the result:
| // ... lines 1 - 9 | |
| class DragonTreasureResourceTest extends KernelTestCase | |
| { | |
| // ... lines 12 - 14 | |
| public function testGetCollectionOfTreasures(): void | |
| { | |
| // ... lines 17 - 18 | |
| $this->browser() | |
| // ... line 20 | |
| ->dump() | |
| // ... line 22 | |
| ->assertJsonMatches('"hydra:totalItems"', 5) | |
| // ... line 24 | |
| ; | |
| } | |
| } |
Try it now:
symfony php bin/phpunit
It passes! And if you look up, yea! The response has 5 treasures! Dang, that was easy.
Next: let's use JMESPath to assert something more challenging. Then we'll back up and see how we can dig into Browser to give us infinite flexibility - and simplicity - when it comes to testing JSON.
13 Comments
For those who cannot see any dump after executing tests like me, remember to add into config file debug.yaml
Hey Abraham,
Thanks for this tip!
Cheers!
this statement : '
->assertJsonMatches('"hydra:totalItems"', 5)'generate this error:
My dump json reveal that it has data.
Fixtures launch correctly.
And I also try this : '
->assertJsonMatches('"hydra:totalItems"', '5')'I will investigate this later but if anyone have a explication now he is welcome : )
although the
'5'is type casting your integer to a string... I got the samenullerror when trying to get the value fromhydra:totalItems. so I was looking into the dump and noticed, that currently the property has changed tototalItemsonly. same formemberHey @neostar
Why are you adding additional quotes? IIRC it should be like this
->assertJsonMatches('hydra:totalItems', 5)Cheers!
according to this chapter (see video around 2:20) and the transcript you have to add quotes when dealing with a colon.
see also tests/Functional/DragonTreasureResourceTest.php line 29 or 30 in the "finished" version.
whoops sorry =) my bad
Have you tried
->dump()to check the output?Cheers!
see the comment further up.. it's not about the quotes in the first place. but the fact that the
hydra:prefix is not existing anymore in the newest version.Yes, but I consider that you are all using API Platform bundled with course, which is based on version 3.1, and no one mentions upgrade to the latest version of it.
fair point. at least if somebody is running into the same problem, they will find 2 possible reasons here now.
Agree! Thanks for cooperation!
Have a good day. Happy codding! Cheers!
Hi everyone !
For me, this, it is not working :
I've got this error :
PDOException: SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: dragon_treasure.owner_idSo, let's do it :
Voilà : It's alive :-)
OK (1 test, 3 assertions)I'm using SQLite, I don't know if it matters.
Hey @DrT
Yes, you need to set the
ownerfield somehow to get rid of that error. In the tutorial we do this inside theDragonTreasureFactory::getDefaults()methodCheers!
"Houston: no signs of life"
Start the conversation!