---
title: 'API Platform Crash Course Part 13: Testing a REST API using PHPUnit'
source: 'https://youtube.com/watch?v=Pj7qyrsGleY'
video_id: 'Pj7qyrsGleY'
date: 2026-06-17
duration_sec: 0
---

# API Platform Crash Course Part 13: Testing a REST API using PHPUnit

> Source: [API Platform Crash Course Part 13: Testing a REST API using PHPUnit](https://youtube.com/watch?v=Pj7qyrsGleY)

## Summary

This tutorial demonstrates a complete test setup for a Symfony API Platform REST API using PHPUnit. The creator walks through creating a dedicated test database, loading fake fixtures with Alice, and writing automated tests to verify CRUD endpoints, pagination, JSON schema, and validation rules—all without leaving the code editor.

### Key Points

- **Goal of the video** [0:00] — The video focuses on setting up API testing with a separate test database, using packages for request simulation and JSON schema assertions.
- **Why a separate test database** [0:22] — A separate test database prevents polluting the dev DB and ensures every test starts from the same clean state, avoiding failures caused by other developers' data.
- **Creating the test database** [1:46] — Command: `symfony console --env=test doctrine:database:create`. This appends '_test' to the database name (e.g., `products_api_test`).
- **Migrating the test schema** [2:30] — Command: `symfony console --env=test doctrine:migrations:migrate` creates the tables (manufacturer, product) in the test DB.
- **Required packages** [3:01] — Installed via Composer: `alice` (fixture generation), `symfony/test-pack` (PHPUnit + bridge), `symfony/http-client` (request simulation), `json-schema` (JSON schema assertions).
- **Setting up test fixtures** [3:56] — YAML fixture files for Manufacturer and Product define entity classes, field values (e.g., company name, description, country code, date). Products reference Manufacturers using notation like `manufacturer: '@manufacturer_*'`.
- **Loading fixtures into test DB** [6:05] — Command: `symfony console --env=test doctrine:fixtures:load`. Must run in `test` environment to avoid populating the dev database.
- **Writing the first test class** [7:08] — Create `products_test.php` in `/tests/`. Extend `ApiTestCase` (from API Platform) and use `RefreshDatabaseTrait` to purge/fixture-load before the first test and wrap all tests in a rollback transaction.
- **Testing GET collection endpoint** [8:25] — Test `test_get_collection`: create client, send GET to `/api/products`, assert 200 response, check `Content-Type` header (`application/ld+json; charset=utf-8`), and validate JSON subset (context, ID, hydra:view).
- **Asserting JSON subset (hydra:view)** [9:56] — Using `assertJsonContains()` with an array that includes `@context`, `@id`, and pagination keys (`hydra:view` → `@id`, `type`, `hydra:first`, `hydra:last`, etc.).
- **Failing test – pagination mismatch** [10:32] — Default pagination differs from expected; fixed by adding `pagination_items_per_page=5` attribute on the Product API resource.
- **Silencing deprecation notices** [11:22] — Add `<server name='SYMFONY_DEPRECATIONS_HELPER' value='disabled'/>` to `phpunit.xml.dist` under `<php>` to remove deprecation output.
- **Counting returned items** [12:09] — Use `assertCount(5, $response->toArray()['hydra:member'])` to verify the page returns exactly 5 products.
- **Testing pagination links** [13:01] — Test `test_pagination`: request `/api/products?page=2`, assert that `hydra:view` contains `@id` with page=2, `hydra:first`, `hydra:last`, `hydra:next` (page 3), and `hydra:previous` (page 1).
- **Testing POST (create product)** [14:40] — Test `test_create_product`: POST to `/api/products` with JSON body (mpn, name, description, issueDate, manufacturer: '/api/manufacturers/1'). Assert 201 status and check response body contains the same fields.
- **Making issueDate writable** [16:54] — Noticed issueDate was not writable in the entity → added `@ApiProperty(writable=true)` or `product:write` group to allow POSTing it.
- **Testing PUT (update product)** [17:55] — Test `test_update_product`: PUT to `/api/products/1` with updated description, assert 200 and that the returned JSON reflects the change.
- **Testing validation – missing manufacturer** [18:45] — Test `test_create_invalid_product`: POST with `manufacturer: null`. Initially got 201 (created), revealing missing `NotNull` constraint. Adding `@Assert
otNull` on manufacturer fixed it, making the test expect 422.
- **Debugging with tests** [19:58] — The test revealed that manufacturer writable group was missing. Adding `manufacturer` to the write group or using `@ApiProperty(writable=true)` allowed the POST with a manufacturer IRI to work.
- **Final takeaway – TDD-like workflow** [21:36] — Tests can drive development: stay in code editor, write tests, fix code based on failures, and avoid constantly switching to Postman or UI.

### Conclusion

The video demonstrates a complete testing workflow for a Symfony API Platform app using PHPUnit, including database isolation, fixture loading, and endpoint validation. End-to-end tests catch bugs early and allow developers to build and refine APIs without leaving the IDE.

## Transcript

in this one we're going to have a look
at testing our api so we'll get a nice
test setup created we'll have a test
database we'll pull in some packages
which are going to help us
make those requests or simulate requests
to our api and also
make assertions against the schema or
the json which we get back so the first
thing we're going to do is actually set
up our test database because i think
it's important to have a separate test
database not essential but it means that
you're not polluting your development
database with test
data and also it means we can have a
setup which starts in the exact same
state for every test so that you don't
have other developers are like adding
new records in there which make your
test fail things like that choose high
definition for the best viewing
experience and if you'd like to join a
growing group of software developers and
take your skills to new level all you
need to do is subscribe click the little
notification icon and welcome now you
may or may not be familiar with symphony
already but i'll show you how the test
database
setup works in there so if we go back
and have a look at our emv file where we
determined our database url here and we
said that our database name will be
products api if we go over to the config
folder here
and in packages
test
doctrine dot yaml you'll see that we
have db name suffix so ignore this part
on the end here this environment
variable thing but what we're looking at
is this so what that means it will
append
the word underscore test onto the end of
your database and so we're gonna just
run a couple of commands and that will
create us a test database with that name
so back to our notes the command that we
need in order to create our test
database is this one so i'll paste it in
my terminal and i'll explain this to you
so symfony console is all the same as
when we created our development database
except here after symphony console we're
saying
emv equals test and then by doing that
it will then create us a test
environment database let's hit go
okay and so he's saying he's created
products api test database i'm going to
go over to table plus and just make sure
that i have that there
okay and so here's my products api test
database obviously there's no tables in
there yet because we need to go and
migrate our schema let's go and do that
next so the next command is this one
symphony console and again we're passing
m test and it's just a doctrine
migrations migrate i'll copy them copy
that paste that in there
run that
and it asks me if i'm sure i'll just say
yes
okay and so it's saying that it's
migrating my database let's go back to
table plus refresh
great stuff so now we have a
manufacturer table and a product table
the next thing i'm going to do is i'm
going to composer require some packages
and they are alice
uh because we're using simplyflex we can
just use a short name for these so alice
symphony test pack symphony http client
and something called json schema so what
are these alice is used to generate fake
fixture data
symphony test pack includes php unit and
php unit bridge
http client is what the api platform
test client is built on so http client
is a symphony component for making
requests and json schema provides json
schema test assertions okay so let's go
and pull that in
[Music]
the next thing i'm going to do is set up
some test fixtures so inside of a
fixtures folder which you see here just
gonna add a yaml file for the
manufacturer and one for the product
and so i'll just paste in something
which i've previously created should be
self-explanatory this is saying that
this is the entity or the model that
this is based on this is what can be
used uh to reference um each particular
record so we'll be able to reference
manufacturer from our product fixtures
and then here is how we create each of
the fields so this is just going to give
it a company name
this will just provide some text for the
description uh it will generate a
country code for the country code and a
date time for the listed date so now
let's go over to our product fixture or
product yaml file and we'll do the same
here
okay so let me just talk you through
this for it's the same as what we did
for the manufacturer so again our entity
is the product and then we're going to
create a hundred this time so what this
is is uh we're creating products from
number one to 100 mpn i've just used
isbn because that's probably the closest
thing i can think of that there's no
no special
item in faker in order to generate
mpn so i'm just using that uh name i've
just gone with catchphrase these things
aren't really that important description
just creating a sentence made up of six
words uh issue date again using the date
time manufacturer because if you think
about our products we can't create
products without manufacturer so what
this will do is it will just reference
manufacturers from our manufacturer yaml
file here so where we're saying
manufacturer underscore 1 to 10 what
this will do is it will reference
one of those for each of the products
i'll go back to my notes file and so we
have a command here which will load our
fixtures that we just created so grab
this
and then i'll go and paste this in here
and again make sure that you are in test
environment when you run this otherwise
you'll end up populating your
development database okay let's hit go
and i'll ask you if you want to continue
okay so these notices here there's just
a deprecation notice i'll show you how
we can get rid of these for our tests
but it's not come back with any errors
so that's telling me that this has
actually worked first time which is a
bit of a miracle because
that has rarely happened before refresh
okay great stuff so it looks like we
have 100 products perfect and we also
have 10 manufacturers we'll just check
the manufacturer ids on our products yep
they run one to ten okay so that's
worked uh perfectly first time great
stuff we're now ready to actually go and
start writing our tests i think
so the folder is tests and in here we'll
just create one test so i would normally
split things down into like product uh
sorry unit test and feature test and
things like that but i'm only going to
create one test here because i'm just
showing you how you can do this uh if
you're building your own api then
obviously you'll go and
put a bit more effort into your folder
structures and things like that so we'll
call this products test
[Music]
and this will extend a test case
called api test case
and that will give us uh some of the
tools we need so things like being able
to simulate the http request we'll have
a client and also some uh api specific
assertions will be available to us in
order for all tests to start the same
state we're going to pull in a little
trait here called refresh database trait
and we're just going to have a little
look in the comments and see what it
says about this basically saying that it
purges and loads the fixtures uh before
the first test and wraps all the tests
in the transaction that we roll back
when it is finished so that means that
each test in our
products test will start in exactly the
same state with the same
fixtures loaded
okay so i'm going to add a test for
getting a collection so test
get
collection so here we're gonna get a
collection of products
and the way i can do that is like this
so static
and then
create client so that will give us our
http client and then we're going to send
a request which will be
a get request so the method is the first
argument the second argument is the url
so that will be for us api
products i can check for a successful
response like this
assert response is successful and that's
all i need to do there i can check
headers
[Music]
so assert response header same
and so we shall say content
type
and i expect it to have this value here
application for slash ld plus json
semicolon
character set is utf-8 utf-8
and then i can actually make assertions
about the body so
this
assert
jason i'm looking for adjacent contain
so i'm not looking for an exact match
i'm just making sure that the json which
comes back does contain a subset which i
specify
and so here
you can pass it in array format and i'm
just going to paste in
some work which i've previously done
because you don't want to see me type
all this out and so we're asserting that
our json should contain this subset here
so it's all the stuff that you've seen
using postman using the user interface
thing we're looking at our context our
id and then we've also got stuff
regarding our pagination here under the
hydra view
and so i'm going to run this test
initially but i'm expecting this to fail
because i've not actually set up
pagination on my product yet so let's go
and run this
[Music]
okay and so look for our failure and
here we're getting uh incorrect
pagination because it's just my product
is currently using in default pagination
so in order to be able to test that and
to demonstrate it i really want to
set up my own pagination so we'll go
over to product
and here under our api resource i'm just
going to add a new one and i'm just
going to borrow the one from
manufacturer so attributes pagination
items per page is 5 which should
make my test pass so we should now get
20 pages in total the last page should
be 20 because 500 sorry 100 divided by 5
should give us 20. let's go and run this
okay and this time we are green so
you'll see that we're getting these
deprecation notices here which i
mentioned earlier if we go to our notes
i've actually got a piece or a line
which we can add to our php unit file so
go over to phpunit xml dist which you'll
find in your project root
and just in here see where you see have
the php tags and you have these server
variables just drop that underneath
there
and all saying is just disable our
symphony deprecations helper let's go
and run this again
okay so this time we get our one test
three sessions but we don't have those
uh annoying deprecation notices in the
way
let's go and make an assertion about the
number of items which are returned so in
order to do this i'm just going to grab
a response here
and what i'll do because we're filling
up our page a bit is i'll make my font a
little bit smaller so we'll go with
22
okay and then just down here
i can say this
assert
count
because we're counting the items in an
array
we should get five items and if i say
response
to array it will take our response and
convert it to an array and then we can
count certain items and we're going to
count hydra members
okay one test for assertions this time
so i'm happy with that test let's now go
and test pagination so we'll hit another
endpoint except this time we'll head for
uh page two will append page two or page
three or something on the end of that so
[Music]
test
pagination
[Music]
okay i'm gonna go and borrow some of
this stuff here
so
uh
i'll go and grab it all but we'll just
edit out the stuff that we don't really
need all i want to look at here really
is the keys that we get here for our
pagination so we're going to make the um
request we'll get a response i'll not
bother checking headers we know that all
that stuff works what we're going to do
here though is say
page equals two
and so that means that some of these
things will change we're getting back
page two
first will remain the same last will
remain the same next will be three but
we also want a previous so
i'll just drop that down there i'll say
hydra
previous
and that will be page
one
and i'll not bother doing a count
so yeah like i say basically i'm just
looking to make sure that we get the
correct pagination details back let's go
and run this
okay to test five assertions let's just
change some of these values change this
to 18 for example just to make sure
okay and it failed so that's good
change that back and we'll test
something else let's create a product
test create product
okay so similar to making a get request
all the uh
we require the http client we make the
request that's up this time we're going
to make a post request so request and
the method will be post and so we're
going to need some more information this
time the url
will be
the same so posting to api
[Music]
products
but now we're going to need an array and
we're going to say json
and then we're going to pass an array
of all of the properties all the fields
which we will which we wish to fill on
our newly created product
so i've just come and pasted some stuff
in there behind the scenes
all the usual stuff mpn name description
issue date the only risky
strategy i'm taking here is i'm making
the assumption that we have a
manufacturer with an id of one in our
test fixtures which we created so i do
know that i have that however if we were
to refresh this or run this again then
because it's a auto increment in id
would actually start from 11 months so
it means that i wouldn't have a
manufacturer id of one so i'm not going
to change it i'm going to leave it the
way it is but it's just to let you know
that this is probably
could be done better the way you could
do it better will be to actually make a
request actually send a request off to
retrieve a manufacturer and just use the
um id for the first manufacturer that
you get back but that's just a little
hint or a little tip there that you
could use
this time our response code should be a
201 so this
assert
set response status code at the same two
zero one
headers should be the same as the last
test so or the first test so we'll just
go and grab that
okay so while i was recording this i
noticed an issue and that was that the
issue date wasn't writable which meant
that we weren't going to
be able to write that to the database
when we did this so go over to your
product and make the issue date also
writeable by saying product write we'll
make some assertions about the response
body so again a cert
json contains
and so we're just looking to make sure
that all these things are correct the
only thing i've made a bit different
here is that this is a format that the
issue date is going to come back in even
though we sent it off in this format so
um just something to be aware of there
let's actually go and run this and see
how we get on
okay so we get three tests eight
assertions so you should be getting the
gist of this now rather than see me type
out these tests in full what i'll do is
i'll just paste them in and explain my
way through them as we go
okay this time we're testing that we can
update a product so test update product
setup is the same
client create client we're making a put
request but this time notice we've
appended on
a
id for the product which we want to
update whereas when we created a product
and we just needed to hit the endpoint
api products this time we need to
specify which product is going to be so
i've gone with api products number one
and then
we're just gonna update the description
so here we're saying an update is
description
and then check that we get a successful
response and then we're just checking
the json which comes back let's go and
run this
okay so that's good then we'll do one
final test to we'll try and actually uh
trigger some validation errors by
sending incorrectly formatted uh body
content or something like that and so
this is the test that i've created for
this test create invalid product and
i've picked this uh case specifically
because i noted i noticed a mistake in
my code and so what i'm going to do is
i'm going to go and fix that but i'm
going to try without using a test so
here i'm saying static create client
we're making a post request to products
and then we're going to create
a product and this time i'm passing
manufacturer null which really we should
have to pass a manufacturer in order to
create a product record because a
product must have a manufacturer but
let's just go and see what happens when
i run this
okay so i'm expecting a validation error
so here this search response status code
same four two two however that's not
what i'm getting back it's saying failed
asserting that
the response status code is 422 if you
notice here the response status code is
201 created which means it's been able
to go ahead and actually create that
record which tells me that there's a
problem with my code so the first thing
i'm going to do is i'm going to go over
to product and just have a look at this
and i'm going to put an not null
assertion in here so assert not null
we'll drop that in here
okay and then we'll go and run the tests
again and see what it tells us
okay and this time we have another
failure
failed asserting that response status
code is 201
on line 68 of products test so what's
going on here let's go over to products
test and we're looking at line 68
so here
where we're saying assert response
status code same two or one that's not
what we're actually getting we're not
actually creating uh that record
and the reason is even though we're
passing a
manufacturer's string here we haven't
actually enabled right
for manufacturer on our product so what
i'm going to do is we're just going to
go and
add that now
and we'll tidy this up a bit because
it's getting a little bit messy we'll
drop this onto its own line
okay back to our test let's run it and
see where we get this time
and so that tells us that we are getting
a two or one saying the product has been
correctly created and it's also being
correctly created with that manufacturer
id as well
and so we'll leave it there hopefully
that's been helpful you've probably been
able to see that you could actually
build your entire application just by
using tests it means we don't actually
have to keep going over to postman or
going over to the user interface we can
stay in the same places where our code
lives just write our tests go make any
changes we need to make to our entities
to our attributes or things like that
also when the test lets us know that
something's going wrong
if you've enjoyed this video and you'd
like youtube to show you more of my
content all you need to do is subscribe
and click the notification icon each
week i release a number of new
recordings if you'd like to be informed
about my upcoming videos as well as
receive exclusive content discounts and
early access to my new videos you can
join my mailing list by following the
link underneath this video
