A set of useful tools to specify and test your API are pre-configured in the API Platform distribution:
- PHPUnit allows to cover your classes with unit tests and to write functional tests thanks to his Symfony integration.
- Behat (a Behavior-driven development framework) and its Behatch extension (a set of contexts dedicated to REST API and JSON documents) are convenient to specify and test your API: write the API specification as user stories and in natural language then execute these scenarios against the application to validate its behavior.
Take a look at the Symfony documentation about testing to learn how to use PHPUnit in your API Platform project.
Here is an example of a Gherkin feature file specifying the behavior of the bookstore API we created in the tutorial. Thanks to Behatch, this feature file can be executed against the API without having to write a single line of PHP.
# features/books.feature
Feature: Manage books and their reviews
In order to manage books and their reviews
As a client software developer
I need to be able to retrieve, create, update and delete them trough the API.
# the "@createSchema" annotation provided by API Platform creates a temporary SQLite database for testing the API
@createSchema
Scenario: Create a book
When I add "Content-Type" header equal to "application/ld+json"
And I add "Accept" header equal to "application/ld+json"
And I send a "POST" request to "/books" with body:
"""
{
"isbn": "9781782164104",
"title": "Persistence in PHP with the Doctrine ORM",
"description": "This book is designed for PHP developers and architects who want to modernize their skills through better understanding of Persistence and ORM.",
"author": "Kévin Dunglas",
"publicationDate": "2013-12-01"
}
"""
Then the response status code should be 201
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/Book",
"@id": "/books/1",
"@type": "Book",
"id": 1,
"isbn": "9781782164104",
"title": "Persistence in PHP with the Doctrine ORM",
"description": "This book is designed for PHP developers and architects who want to modernize their skills through better understanding of Persistence and ORM.",
"author": "K\u00e9vin Dunglas",
"publicationDate": "2013-12-01T00:00:00+00:00",
"reviews": []
}
"""
Scenario: Retrieve the book list
When I add "Accept" header equal to "application/ld+json"
And I send a "GET" request to "/books"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/Book",
"@id": "/books",
"@type": "hydra:Collection",
"hydra:member": [
{
"@id": "/books/1",
"@type": "Book",
"id": 1,
"isbn": "9781782164104",
"title": "Persistence in PHP with the Doctrine ORM",
"description": "This book is designed for PHP developers and architects who want to modernize their skills through better understanding of Persistence and ORM.",
"author": "K\u00e9vin Dunglas",
"publicationDate": "2013-12-01T00:00:00+00:00",
"reviews": []
}
],
"hydra:totalItems": 1
}
"""
Scenario: Throw errors when a post is invalid
When I add "Content-Type" header equal to "application/ld+json"
And I add "Accept" header equal to "application/ld+json"
And I send a "POST" request to "/books" with body:
"""
{
"isbn": "1312",
"title": "",
"description": "Yo!",
"author": "Me!",
"publicationDate": "2016-01-01"
}
"""
Then the response status code should be 400
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/ConstraintViolationList",
"@type": "ConstraintViolationList",
"hydra:title": "An error occurred",
"hydra:description": "isbn: This value is neither a valid ISBN-10 nor a valid ISBN-13.\ntitle: This value should not be blank.",
"violations": [
{
"propertyPath": "isbn",
"message": "This value is neither a valid ISBN-10 nor a valid ISBN-13."
},
{
"propertyPath": "title",
"message": "This value should not be blank."
}
]
}
"""
# The "@dropSchema" annotation must be added on the last scenario of the feature file to drop the temporary SQLite database
@dropSchema
Scenario: Add a review
When I add "Content-Type" header equal to "application/ld+json"
When I add "Accept" header equal to "application/ld+json"
And I send a "POST" request to "/reviews" with body:
"""
{
"rating": 5,
"body": "Must have!",
"author": "Foo Bar",
"publicationDate": "2016-01-01",
"book": "/books/1"
}
"""
Then the response status code should be 201
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/Review",
"@id": "/reviews/1",
"@type": "Review",
"id": 1,
"rating": 5,
"body": "Must have!",
"author": "Foo Bar",
"publicationDate": "2016-01-01T00:00:00+00:00",
"book": "/books/1"
}
"""
The API Platform flavor of Behat also comes with a temporary SQLite database dedicated to tests. It works out of the box.
Clear the cache of the test
environment:
$ docker-compose exec web bin/console cache:clear --env=test
Then run:
$ docker-compose run --rm web vendor/bin/behat`.
Everything should be green now. Your Linked Data API is now specified and tested thanks to Behat!
You may also be interested by those alternative testing tools (not included in the API Platform distribution):
- Postman tests (proprietary): create functional test for your API Platform project using a nice UI, benefit from the Swagger integration and run tests test in the CI using newman.
- PHP Matcher: the Swiss Army knife of JSON document testing.
Previous chapter: Creating your First API with API Platform, in 5 Minutes
Next chapter: API Platform Core: Introduction