Backport the API Platform 2.5 Test Tools

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.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

To test our API, we're going to use PHPUnit and some other tools to make a request into our API, get back a response, and assert different things about it, like the status code, that the response is JSON and that the JSON has the right keys.

API platform recently introduced some really nice tools for doing all this. In fact it was so recent that... they're not released yet! When I run:

composer show api-platform/core

...you can see that I'm using Api Platform 2.4.5. These new features will come out in version 2.5. So if you're already on version 2.5, you can skip to the end of this video where we bootstrap our first test.

But if you're with me on 2.4.5... welcome! We're going to do some hacking and backport those features manually into our app. If you downloaded the course code, you should have a tutorial/ directory inside. If you don't have this, try downloading the code again to get it.

Copying the new Test Classes

See this Test/ directory? This contains a copy of all of those new testing classes. The only difference is that I've changed their namespaces to use App\ApiPlatform\Test instead of what they look like inside of ApiPlatform: ApiPlatform\Core\Bridge\Symfony\Bundle\Test.

That's because we're going to move all of this directly into our src/ directory. Start by creating a new ApiPlatform/ directory... and then copy Test/ and paste it there.

I'm also going to right click on the tutorial/ directory and say "Mark as Excluded". That tells PhpStorm to ignore those files... which is important so it doesn't get confused by seeing the same classes in two places.

Registering the new Test Client Service

Beyond the new classes, we need to make one other change. Open up config/ and create a new file: services_test.yaml. Thanks to its name, this file will only be loaded in the test environment. Inside, add services: and create a new service called test.api_platform.client: with class: App\ApiPlatform\Test\Client and arguments: ['@test.client']. Also add public: true.

services:
# added so we can use the new API Platform test tools before
# they are released. In API Platform 2.5, this won't be needed.
test.api_platform.client:
class: App\ApiPlatform\Test\Client
arguments: ['@test.client']
public: true

These two steps completely replicate what ApiPlatform will give you in version 2.5. Well... unless they change something. I don't normally show unreleased features... because they might change... but these tools are so useful, I just had to include them. When 2.5 does come out, there could be a few differences.

Bootstrapping the First Test

Ok, let's create our first test! Inside the tests/ directory, create a functional/ directory and then a new class: CheeseListingResourceTest.

This isn't an official convention, but because we're functionally testing our API... and because everything in API Platform is based on the API resource, it makes sense to create a test class for each resource: we test the CheeseListing resource via CheeseListingResourceTest.

Make this extend ApiTestCase - that's one of the new classes we just moved into our app. If you're using API Platform 2.5, the namespace will be totally different - it'll start with ApiPlatform\Core.

... lines 1 - 2
namespace App\Tests\Functional;
use App\ApiPlatform\Test\ApiTestCase;
class CheeseListingResourceTest extends ApiTestCase
... lines 8 - 14

The first thing I want to test is the POST operation - the operation that creates a new CheeseListing. A few minutes ago, under collectionOperations, we added an access control that made it so that you must be logged into use this. Oh... and I duplicated the collectionOperations key too! The second one is overriding the first... so let's remove the extra one.

Anyways, for our first test, I want to make sure this security is working. Add public function testCreateCheeseListing(). And inside, make sure this all isn't an elaborate dream with $this->assertEquals(42, 42).

... lines 1 - 8
public function testCreateCheeseListing()
{
$this->assertEquals(42, 42);
}
... lines 13 - 14

Ok! Run that with:

php bin/phpunit

We're alive! Next, let's turn this into a true functional test against our API! We'll also take care of some other details to make our tests shine.

Leave a comment!

  • 2020-01-09 Diego Aguiar

    Hey SirRFI, sorry for the late response

    I found this in the ApiPlatform docs: https://api-platform.com/do...
    but it doesn't say anything of how to port the testing utilities to your application. So, I believe it won't be that easy as installing a library via composer. What you can do is basically what you did, copy-paste the ApiTestCase class from the ApiPlatform project and make your tests extend from it
    You may want to ask for this to the ApiPlatform team, I think it's possible for them to offer the testing utilities as a separate component

    Cheers!

  • 2020-01-07 SirRFI

    Thanks for reply Diego Aguiar, but this is not what I asked. Sorry if my message was unclear, let me try fix that:

    I am aware this course is about ApiPlatform and that the code was backported from then unreleased 2.5 to 2.4, which is no longer required to do when using ApiPlatform 2.5 or newer. The project I mentioned does not use ApiPlatform, but regular Symfony 4.4.

    Functional testing in Symfony, as described in the documentation, uses client from BrowserKit component. It is said to be attached to the framework's kernel, so no actual requests are made during the tests. To my understanding, it causes everything to happen internally in PHP, without the need to having actual server running and extra configuration. As result, it's easier to use and significantly more performant, which is cool.

    However, I spent few hours working with the tests as described in the docs, and found the client to be confusing/unclear to use. Given my previous positive experience with HttpClient component and examples in following chapters of this course, I wondered if it's possible to use it in tests instead of BrowserKit's.

    The backported code in this chapter allows to use HttpClient component, while keeping the benefits described above. According to what I was told just recently, it still uses BrowserKit underneath, as if only interface changed to HttpClient's. It also brings ApiPlatform exclusive methods (related to schemas for example) and some extra assertions.

    This sounds like something I am looking for: the same good tests, but with better interface. So, I gave it a try and ported the files to my project - and they do work. Well, I had to remove some parts that wouldn't work, like the schema parts.

    While it does work, it's still some kind of workaround as well, so I wonder if there's cleaner way to do it in Symfony project. Some inbuilt way, or a component? The extra assertions, like JsonContains are also convenient.

    Please correct me if I am wrong somewhere.

    Why even bother You may ask? Aside from HttpClient's interface being easier and more readable to me, the other components from test-pack seems to be focused around crawling/scrapping, which I simply don't need for testing API endpoints.

  • 2020-01-06 Diego Aguiar

    Hey SirRFI

    I believe the HttpClient component is your best shot for testing api endpoints in Symfony. Also, all the vendor files copied was just to workaround a problem with ApiPlatform 2.4 version, if you use 2.5+ version then you shouldn't need to copy any files

    Cheers!

  • 2020-01-05 SirRFI

    Wanting to write my first functional test in Symfony 4.4 app (not APIP), I followed the "Testing" page in Symfony Docs. It focuses around components included in `test-pack`, that apparently were built for crawling/scrapping. My app has only API endpoints, so it's reduntant. Also, I found `BrowserKit`'s client confusing to use.

    Can I somehow write functional tests using `HttpClient` component, while keeping the benefits of "fake http client" (doing requests internally, in PHP only)? To my surprise, following steps from this chapter do work in normal Symfony project. Nonetheless, it's still several files literally taken from vendor, with some methods that won't work. So, I wonder if there's a cleaner way to achieve this - any unbuild or another component? Also the `assertJsonContains` `assertJsonEquals` methods seem nice.

  • 2019-12-16 Diego Aguiar

    Thanks for sharing it man!

  • 2019-12-13 Дилян Траянов

    If you are using the downloaded code as I am, you can update only the api-platform/core package to 2.5.0 (compser update api-platform/core). And if you are using PHPStorm (You should), exlude the "tutorial" directory. IDE will include the proper use statement when extending from ApiTestCase.