I have an endpoint, that allows file upload, everything works fine.
Next thing is to cover the endpoint with proper functional test.
And here's the problem - I can't pass the file
to the client making the request.
My test class extends \ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase
.
static::createClient()
method creates an instance of ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client
and these Client
does not support file uploads.
Beacuse of implementing the Symfony\Contracts\HttpClient\HttpClientInterface
which defines public function request(string $method, string $url, array $options = []): ResponseInterface;
there's no place for passing files
argument.
The allowed options
in Client
does not support files
array.
Internaly it looks like this:
ApiPlatform\Core\Bridge\Symfony\Bundle\Test\Client::request
passes to the internal kernelBrowser
an empty array in place of files
params (2nd array): $this->kernelBrowser->request($method, $resolvedUrl, [], [], $server, $options['body'] ?? null)
How do you test endpoints with file upload by extending Base class for functional API tests
which is ApiTestCase
?
Here's some code, to help you visualize the problem:
ApiResource definition in entity:
/**
* @ApiResource(
* collectionOperations={
* "file_upload"={
* "method"="post",
* "controller"=FileUpload::class,
* "path"="/api/file-upload-endpoint",
* "deserialize"=false,
* "openapi_context"={
* "requestBody"={
* "content"={
* "multipart/form-data"={
* "schema"={
* "type"="object",
* "properties"={
* "file"={
* "type"="string",
* "format"="binary"
* }
* }
* }
* }
* }
* }
* }
* },
* },
* )
*/
Test class (don't mind the instance of UploadedFile
, it's just there, to show you, that it cannot be passed anywhere):
<?php
declare(strict_types=1);
namespace App\Tests\Api;
use \ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile;
final class FileUploadTest extends ApiTestCase
{
public function testFileUploadSuccessfully():void
{
$file = new UploadedFile(
TESTS_PROJECT_DIR.'/tests/files/Small_sample_of_jet.jpg',
'Small_sample_of_jet.jpg',
'image/jpeg',
);
static::createClient()->request(
'POST',
'/api/file-upload-endpoint',
[
'headers' => [
'Content-Type' => 'multipart/form-data',
],
],
);
self::assertResponseIsSuccessful();
self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
}
}
And here is what i'm looking for:
<?php
declare(strict_types=1);
namespace App\Tests\Api;
use \ApiPlatform\Core\Bridge\Symfony\Bundle\Test\ApiTestCase;
use Symfony\Component\HttpFoundation\File\UploadedFile;
final class FileUploadTest extends ApiTestCase
{
public function testFileUploadSuccessfully():void
{
$file = new UploadedFile(
TESTS_PROJECT_DIR.'/tests/files/Small_sample_of_jet.jpg',
'Small_sample_of_jet.jpg',
'image/jpeg',
);
static::createClient()->request(
'POST',
'/api/file-upload-endpoint',
[
'headers' => [
'Content-Type' => 'multipart/form-data',
],
],
[
'file'=>$file
]
);
self::assertResponseIsSuccessful();
self::assertResponseHeaderSame('content-type', 'application/ld+json; charset=utf-8');
}
}
When modyfing the vendor
itself and passing the files
to the Client::request
and then to the kernelBrowser
in place of 2nd empty array, everything works fine (I'm aware of breaking the contract, that's not the issue here ;)).
I'm thinking if there's missing feature of uploading files in ApiTestCase
or I just can't find the solution.
Pls halp!
Api Platform version: 2.5.6
PS: I know i can use different client - test.client
$client = static::$kernel->getContainer()->get('test.client');
which is an instance of Symfony\Bundle\FrameworkBundle\KernelBrowser
, the same that is used internally by the Api Platform's Client
and that supports files
array, but that's not the point of my question. I'd like to know how to do file upload with ApiTestCase
.
Since the current latest release of api-platform/core
(2.5.8) we are able to pass more parameters to kernelBrowser->request
via the extra
key. This also now includes files!
Here is a very basic example of testing an image upload (implemented based on the official API Platform documentation):
$file = new UploadedFile(
'path/to/images/my_image.png',
'my_image.png',
'image/png',
);
$response = static::createClient()->request('POST', '/upload_image',
[
'headers' => ['Content-Type' => 'multipart/form-data'],
'extra' => [
'files' => [
'file' => $file,
],
],
],
);