Skip to content

Commit b439c8a

Browse files
committed
[Feature] Add non-JSON API request test helper methods
The test builder now more easily supports testing non-JSON API requests for which a JSON API response is expected. Closes #545
1 parent 9bfbdd8 commit b439c8a

File tree

3 files changed

+153
-22
lines changed

3 files changed

+153
-22
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. This projec
44

55
## Unreleased
66

7+
### Added
8+
- [#545](https://github.com/cloudcreativity/laravel-json-api/issues/545)
9+
The request test builder now supports testing requests that do not have JSON API request content,
10+
but expect a JSON API response. For example, a file upload that results in a JSON API resource
11+
in the response body can be tested using:
12+
`$this->jsonApi()->asMultiPartFormData()->withPayload($data)->post('/api/v1/avatars');`.
13+
714
### Changed
815
- [#497](https://github.com/cloudcreativity/laravel-json-api/issues/497) and
916
[#529](https://github.com/cloudcreativity/laravel-json-api/pull/529)

src/Testing/TestBuilder.php

Lines changed: 143 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use PHPUnit\Framework\TestCase;
1010
use function array_walk_recursive;
1111
use function collect;
12-
use function http_build_query;
1312
use function implode;
1413
use function is_bool;
1514
use function is_null;
@@ -46,7 +45,17 @@ final class TestBuilder
4645
/**
4746
* @var Collection
4847
*/
49-
private $document;
48+
private $headers;
49+
50+
/**
51+
* @var Collection|null
52+
*/
53+
private $json;
54+
55+
/**
56+
* @var Collection|null
57+
*/
58+
private $payload;
5059

5160
/**
5261
* TestBuilder constructor.
@@ -58,7 +67,7 @@ public function __construct($test)
5867
$this->test = $test;
5968
$this->accept = $this->contentType = 'application/vnd.api+json';
6069
$this->query = collect();
61-
$this->document = collect();
70+
$this->headers = collect();
6271
}
6372

6473
/**
@@ -100,6 +109,28 @@ public function contentType(?string $mediaType): self
100109
return $this;
101110
}
102111

112+
/**
113+
* Set the request content type to 'application/x-www-form-urlencoded'.
114+
*
115+
* @return $this
116+
*/
117+
public function asFormUrlEncoded(): self
118+
{
119+
return $this->contentType('application/x-www-form-urlencoded');
120+
}
121+
122+
/**
123+
* Set the request content type to multipart form data.
124+
*
125+
* @return $this
126+
*/
127+
public function asMultiPartFormData(): self
128+
{
129+
return $this->contentType(
130+
'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
131+
);
132+
}
133+
103134
/**
104135
* Add query parameters to the request.
105136
*
@@ -127,7 +158,7 @@ public function includePaths(string ...$paths): self
127158
}
128159

129160
/**
130-
* Set the sparse fieldsets for a resource type.
161+
* Set the sparse field sets for a resource type.
131162
*
132163
* @param string $resourceType
133164
* @param string|string[] $fieldNames
@@ -183,17 +214,40 @@ public function page(iterable $page): self
183214
/**
184215
* Set the data member of the request JSON API document.
185216
*
186-
* @param mixed|null $data
217+
* @param iterable|null $data
187218
* @return $this
219+
* @deprecated 4.0 use `withData`.
188220
*/
189221
public function data($data): self
222+
{
223+
return $this->withData($data);
224+
}
225+
226+
/**
227+
* Set the data member of the request JSON API document.
228+
*
229+
* @param iterable|null $data
230+
* @return $this
231+
*/
232+
public function withData($data): self
190233
{
191234
if (is_null($data)) {
192-
$this->document->put('data', null);
193-
} else {
194-
$this->document->put('data', collect($data));
235+
return $this->withJson(['data' => null]);
195236
}
196237

238+
return $this->withJson(['data' => collect($data)]);
239+
}
240+
241+
/**
242+
* Set the JSON request document.
243+
*
244+
* @param $json
245+
* @return $this
246+
*/
247+
public function withJson($json): self
248+
{
249+
$this->json = collect($json);
250+
197251
return $this;
198252
}
199253

@@ -203,10 +257,11 @@ public function data($data): self
203257
* @param mixed $document
204258
* @param string|null $contentType
205259
* @return $this
260+
* @deprecated 4.0
206261
*/
207262
public function content($document, string $contentType = null): self
208263
{
209-
$this->document = collect($document);
264+
$this->json = collect($document);
210265

211266
if ($contentType) {
212267
$this->contentType($contentType);
@@ -215,6 +270,44 @@ public function content($document, string $contentType = null): self
215270
return $this;
216271
}
217272

273+
/**
274+
* Set the request payload for a non-JSON API request.
275+
*
276+
* @param $parameters
277+
* @return $this
278+
*/
279+
public function withPayload($parameters): self
280+
{
281+
$this->payload = collect($parameters);
282+
// we need a content length as it is used by the JSON API implementation to determine if there is body.
283+
$this->headers['CONTENT_LENGTH'] = '1';
284+
285+
return $this;
286+
}
287+
288+
/**
289+
* @param iterable $headers
290+
* @return $this
291+
*/
292+
public function withHeaders(iterable $headers): self
293+
{
294+
$this->headers = $this->headers->merge($headers);
295+
296+
return $this;
297+
}
298+
299+
/**
300+
* @param string $name
301+
* @param string $value
302+
* @return $this
303+
*/
304+
public function withHeader(string $name, string $value): self
305+
{
306+
$this->headers->put($name, $value);
307+
308+
return $this;
309+
}
310+
218311
/**
219312
* Visit the given URI with a GET request, expecting JSON API content.
220313
*
@@ -251,6 +344,16 @@ public function patch(string $uri, iterable $headers = []): TestResponse
251344
return $this->call('PATCH', $uri, $headers);
252345
}
253346

347+
/**
348+
* @param string $uri
349+
* @param array|iterable $headers
350+
* @return TestResponse
351+
*/
352+
public function put(string $uri, iterable $headers = []): TestResponse
353+
{
354+
return $this->call('PUT', $uri, $headers);
355+
}
356+
254357
/**
255358
* Visit the given URI with a DELETE request, expecting JSON API content.
256359
*
@@ -275,17 +378,24 @@ public function call(string $method, string $uri, iterable $headers = []): TestR
275378
$uri .= '?' . $this->buildQuery();
276379
}
277380

278-
$headers = collect([
279-
'Accept' => $this->accept,
280-
'CONTENT_TYPE' => $this->contentType,
281-
])->filter()->merge($headers);
381+
$headers = $this->buildHeaders($headers);
282382

283-
$response = TestResponse::cast($this->test->json(
284-
$method,
285-
$uri,
286-
$this->document->toArray(),
287-
$headers->toArray()
288-
));
383+
if ($this->payload) {
384+
$response = $this->test->{strtolower($method)}(
385+
$uri,
386+
$this->payload->toArray(),
387+
$headers
388+
);
389+
} else {
390+
$response = $this->test->json(
391+
$method,
392+
$uri,
393+
$this->json ? $this->json->toArray() : [],
394+
$headers
395+
);
396+
}
397+
398+
$response = TestResponse::cast($response);
289399

290400
if ($this->expectedResourceType) {
291401
$response->willSeeResourceType($this->expectedResourceType);
@@ -315,6 +425,19 @@ private function buildQuery(): string
315425
}
316426
});
317427

318-
return http_build_query($query);
428+
return Arr::query($query);
429+
}
430+
431+
/**
432+
* @param iterable $headers
433+
* @return array
434+
*/
435+
private function buildHeaders(iterable $headers): array
436+
{
437+
return collect(['Accept' => $this->accept, 'CONTENT_TYPE' => $this->contentType])
438+
->filter()
439+
->merge($this->headers)
440+
->merge($headers)
441+
->toArray();
319442
}
320443
}

tests/dummy/tests/Feature/Avatars/CreateTest.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,10 @@ public function test(string $contentType): void
4747

4848
$response = $this
4949
->jsonApi()
50+
->includePaths('user')
5051
->contentType($contentType)
51-
->content(['avatar' => $file])
52-
->post('/api/v1/avatars?include=user');
52+
->withPayload(['avatar' => $file])
53+
->post('/api/v1/avatars');
5354

5455
$id = $response
5556
->assertCreatedWithServerId(url('/api/v1/avatars'), $expected)

0 commit comments

Comments
 (0)