I have some API endpoints for file uploads, which response with JSON:API documents.
As far as I'm aware I can not use jsonApi() test builder provided by MakesJsonApiRequests to construct the requests. That one does not seem to support file upload at all. And I think support for requests, which aren't JSON:API compliant, is out of it's scope.
But using CloudCreativity\LaravelJsonApi\Testing\TestResponse is very helpful. As the endpoint response with a valid JSON:API document it makes writing the test way easier. Also the test is more inline with the rest of the test suite.
In v1 I was able to achieve what I need by just doing this:
$this->call('POST', '/v1/documents', $requestPayload, [], $files, []);
->assertCreatedWithId($expectedResponsePayload);
To be honest I'm not sure why this worked but it has worked.
In v2 this was not working anymore. $this->call() returns a Illuminate\Testing\TestResponse, which does not have the assertions provided by TestResponse. assertCreatedWithServerId() which replaced assertCreatedWithId in v2 wasn't available. I ended up casting the request to TestResponse manually to make it work:
TestResponse::cast(
$this->call('POST', '/v1/documents', $requestPayload, [], $files, [])
)
->willSeeResourceType($this->resourceType)
->assertCreatedWithServerId('http://examples.com/v1/documents', $expectedResponsePayload);
But this feels like way more boilerplate and I'm not even sure if casting a response to TestResponse yourself is considered part of the public API.
Is there a better way to do this in v2?
Hi! Yeah, sorry didn't put this change in the upgrade guide because it wasn't documented and didn't think it would be encountered by anyone except me.
Basically, in v1 the MakesJsonApiRequests trait changed the test response class by overloading the createTestResponse() method on the test class. Here it is in action:
https://github.com/cloudcreativity/laravel-json-api/blob/v1.7.0/src/Testing/MakesJsonApiRequests.php#L176-L179
In v2 I removed this because I thought it wasn't safe for a package to be doing this - it's kind of up to the developer what test response class they get on the generic Laravel test methods. As such, in v2 the Laravel JSON API test response class is only returned for JSON API requests - i.e. through the method chaining from $this->jsonApi().
If you want the Laravel JSON API test class returned for non-JSON API test requests, then overload the createTestResponse() method on your test class.
Alternatively, the Laravel JSON API test response class has a static cast() method, as you've worked out.
You're right though that it doesn't feel massively intuitive. Therefore, maybe we should add something to the $this->jsonApi() test request builder to make it easier to do a file upload and get JSON API content in the response?
Maybe:
$response = $this
->jsonApi()
->contentType('multipart/form-data')
->withPayload($payload)
->withFiles($files)
->post($uri);
What do you think?
Therefore, maybe we should add something to the
$this->jsonApi()test request builder to make it easier to do a file upload and get JSON API content in the response?
To be honest I'm not sure how likely my use case is. It would add quiet some complexity to the library, wouldn't it? Not only for supporting file uploads but also for multipart/form-data.
I'm fine with the manual casting the response to CloudCreativity\LaravelJsonApi\Testing\TestResponse. I opened this issue mainly for two reasons:
TestResponse::cast() is considered public API as it's not documented.We have quite a lot of endpoints where the request content type is not JSON API, but the response content type is. So I think supporting that kind of request via the fluent test request builder is a useful thing to support. So will add it in for the next version.
Syntax for testing file uploads will be:
$this->jsonApi()->asMultiPartFormData()->withPayload($data)->post('/api/v1/avatars');
There is also a asFormUrlEncoded() method, and if any other request content type needs to be used the contentType() method can be used.
In the next major release (3.0).
Note it's not necessary to split files out from $data as I've noticed the Laravel test helpers do that already - so $data can contain a mixture of files and non-files as needed.
This looks awesome. 馃ぉ
In the next major release (3.0).
Are you planning to release another major soon?
Yeah there's a major release in the pipeline as I accepted a PR that introduced a breaking change. It's only one change though so the upgrade path isn't very difficult. Going to sort that out to coincide with Laravel 8.