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.php
is required to search JSON
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!
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.
"Houston: no signs of life"
Start the conversation!
// composer.json
{
"require": {
"php": ">=8.1",
"ext-ctype": "*",
"ext-iconv": "*",
"api-platform/core": "^3.0", // v3.1.2
"doctrine/annotations": "^2.0", // 2.0.1
"doctrine/doctrine-bundle": "^2.8", // 2.8.3
"doctrine/doctrine-migrations-bundle": "^3.2", // 3.2.2
"doctrine/orm": "^2.14", // 2.14.1
"nelmio/cors-bundle": "^2.2", // 2.2.0
"nesbot/carbon": "^2.64", // 2.66.0
"phpdocumentor/reflection-docblock": "^5.3", // 5.3.0
"phpstan/phpdoc-parser": "^1.15", // 1.16.1
"symfony/asset": "6.2.*", // v6.2.5
"symfony/console": "6.2.*", // v6.2.5
"symfony/dotenv": "6.2.*", // v6.2.5
"symfony/expression-language": "6.2.*", // v6.2.5
"symfony/flex": "^2", // v2.2.4
"symfony/framework-bundle": "6.2.*", // v6.2.5
"symfony/property-access": "6.2.*", // v6.2.5
"symfony/property-info": "6.2.*", // v6.2.5
"symfony/runtime": "6.2.*", // v6.2.5
"symfony/security-bundle": "6.2.*", // v6.2.6
"symfony/serializer": "6.2.*", // v6.2.5
"symfony/twig-bundle": "6.2.*", // v6.2.5
"symfony/ux-react": "^2.6", // v2.7.1
"symfony/ux-vue": "^2.7", // v2.7.1
"symfony/validator": "6.2.*", // v6.2.5
"symfony/webpack-encore-bundle": "^1.16", // v1.16.1
"symfony/yaml": "6.2.*" // v6.2.5
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.4", // 3.4.2
"mtdowling/jmespath.php": "^2.6", // 2.6.1
"phpunit/phpunit": "^9.5", // 9.6.3
"symfony/browser-kit": "6.2.*", // v6.2.5
"symfony/css-selector": "6.2.*", // v6.2.5
"symfony/debug-bundle": "6.2.*", // v6.2.5
"symfony/maker-bundle": "^1.48", // v1.48.0
"symfony/monolog-bundle": "^3.0", // v3.8.0
"symfony/phpunit-bridge": "^6.2", // v6.2.5
"symfony/stopwatch": "6.2.*", // v6.2.5
"symfony/web-profiler-bundle": "6.2.*", // v6.2.5
"zenstruck/browser": "^1.2", // v1.2.0
"zenstruck/foundry": "^1.26" // v1.28.0
}
}