Skip to content

Commit

Permalink
feat(Http): Add resourceArray to JsonResource/ResourceTestCase
Browse files Browse the repository at this point in the history
- Enables a quick way to build resource to an array reusing set container/request
- ResourceTestCase contains $this->request for faster reuisability
  • Loading branch information
pionl committed Jun 30, 2023
1 parent 968f981 commit 34af3f0
Show file tree
Hide file tree
Showing 5 changed files with 286 additions and 23 deletions.
16 changes: 16 additions & 0 deletions src/Http/Resources/JsonResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Illuminate\Container\Container as LaravelContainer;
use Illuminate\Contracts\Container\Container;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource as BaseJsonResource;

abstract class JsonResource extends BaseJsonResource
Expand Down Expand Up @@ -42,6 +43,21 @@ protected function getContainer(): Container
return $this->container;
}

/**
* Call toArray on the resource with given request. If the resource is a JsonResource or JsonResourceCollection,
* container is set on the resource.
*
* @return array<string|int, mixed>
*/
protected function resourceArray(Request $request, BaseJsonResource $resource): array
{
if ($resource instanceof self || $resource instanceof JsonResourceCollection) {
$resource->setContainer($this->getContainer());
}

return (array) $resource->toArray($request);
}

/**
* @template TInstance of object
*
Expand Down
73 changes: 62 additions & 11 deletions src/Testing/PHPUnit/ResourceTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

use Closure;
use Exception;
use Illuminate\Contracts\Container\Container;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Http\Resources\Json\JsonResource as BaseJsonResource;
use LaraStrict\Http\Resources\JsonResource as LaraStrictJsonResource;
use LaraStrict\Http\Resources\JsonResourceCollection;
use LaraStrict\Testing\Assert\AssertExpectationTestCase;
Expand All @@ -20,6 +22,23 @@
*/
abstract class ResourceTestCase extends AssertExpectationTestCase
{
private ?Request $request = null;

protected function setUp(): void
{
parent::setUp();
$this->request = null;
}

public function getRequest(): Request
{
if ($this->request === null) {
$this->request = $this->createRequest();
}

return $this->request;
}

/**
* @return array<string|int, array{0: Closure(static):void}>
*/
Expand All @@ -37,24 +56,15 @@ protected function assert(mixed $object, array|Exception $expected, ?TestingCont
$this->expectExceptionObject($expected);
}

$request = new Request();

$resource = $this->createResource(is_callable($object) ? $object() : $object);

if ($container !== null) {
if ($resource instanceof LaraStrictJsonResource === false
&& $resource instanceof JsonResourceCollection === false) {
throw self::containerCannotBeSetException();
}

$resource->setContainer($container);
}
$this->tryToSetContainer(container: $container, resource: $resource);

if ($expected instanceof Throwable) {
$this->expectExceptionObject($expected);
}

$result = $resource->resolve($request);
$result = $resource->resolve($this->getRequest());

$this->assertEquals(expected: $expected, actual: $result);
}
Expand All @@ -64,6 +74,28 @@ protected function assert(mixed $object, array|Exception $expected, ?TestingCont
*/
abstract protected function createResource(mixed $object): JsonResource;

/**
* Calls toArray on the resource. If the resource is a LaraStrictJsonResource or JsonResourceCollection, the
* container will be set on the resource (if provided).
*
* By default, the request is set from $this->getRequest() method.
*
* @return array<int|string, mixed>|null
*/
protected function resourceArray(
?BaseJsonResource $resource,
?TestingContainer $container = null,
Request $request = null
): ?array {
if ($resource === null) {
return null;
}

$this->tryToSetContainer(container: $container, resource: $resource);

return (array) $resource->toArray($request ?? $this->getRequest());
}

protected static function containerCannotBeSetException(): LogicException
{
return new LogicException(sprintf(
Expand All @@ -72,4 +104,23 @@ protected static function containerCannotBeSetException(): LogicException
JsonResourceCollection::class
));
}

protected function createRequest(): Request
{
return new Request();
}

protected function tryToSetContainer(?TestingContainer $container, BaseJsonResource $resource): void
{
if ($container === null) {
return;
}

if ($resource instanceof LaraStrictJsonResource === false
&& $resource instanceof JsonResourceCollection === false) {
throw self::containerCannotBeSetException();
}

$resource->setContainer($container);
}
}
131 changes: 131 additions & 0 deletions tests/Unit/Http/Resources/JsonResourceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;
use LaraStrict\Testing\Laravel\TestingContainer;
use LaraStrict\Testing\PHPUnit\ResourceTestCase;
use Tests\LaraStrict\Feature\Http\Resources\LaraStrictResource;
use Tests\LaraStrict\Feature\Http\Resources\TestAction;
use Tests\LaraStrict\Feature\Http\Resources\TestEntity;
use Tests\LaraStrict\Unit\Testing\PHPUnit\LaravelResource;

/**
* @extends ResourceTestCase<JsonResource>
*/
class JsonResourceTest extends ResourceTestCase
{
private const Value = 'val123';
private const KeyRes = 'res';
private const KeyInstance = 'instance';
private const KeyTest = 'test';

public function data(): array
{
$container = new TestingContainer(
makeAlwaysBinding: static fn () => new TestAction(value: '1')
);
$entity = new TestEntity(value: self::Value);
$laraStrictResource = new LaraStrictResource($entity);
$laravelResource = new LaravelResource($entity);
$laravelCollection = LaravelResource::collection([$entity]);
$laraStrictCollection = LaraStrictResource::collection([$entity]);

return [
'sets container to larastrict resource' => [
fn (self $self) => $this->assert(
object: $laraStrictResource,
expected: self::expected(instance: '1'),
container: $container
),
],
'sets laravel container to larastrict resource if not provided' => [
fn (self $self) => $this->assert(
object: $laraStrictResource,
expected: self::expected(instance: 'injected')
),
],
'collection sets container to larastrict resource' => [
fn (self $self) => $this->assert(
object: $laraStrictCollection,
expected: self::expectedCollection(instance: '1'),
container: $container
),
],
'collection sets laravel container to larastrict resource if not provided' => [
fn (self $self) => $this->assert(
object: $laraStrictCollection,
expected: self::expectedCollection(instance: 'injected')
),
],
'does not container to normal resource if provided' => [
fn (self $self) => $this->assert(
object: $laravelResource,
expected: self::expected(instance: null),
container: $container
),
],
'does not container to normal resource if not provided' => [
fn (self $self) => $this->assert(object: $laravelResource, expected: self::expected(instance: null)),
],
'collection does not container to normal resource if provided' => [
fn (self $self) => $this->assert(
object: $laravelResource,
expected: self::expected(instance: null),
container: $container
),
],
'collection does not container to normal resource if not provided' => [
fn (self $self) => $this->assert(
object: $laravelCollection,
expected: self::expectedCollection(instance: null)
),
],
];
}

/**
* @param callable(self):void $assert
*
* @dataProvider data
*/
public function test(callable $assert): void
{
$assert($this);
}

protected function createResource(mixed $object): JsonResource
{
return new ResourceArrayResource($object);
}

/**
* @return array<string, array<string, string>>
*/
private static function expected(?string $instance): array
{
return [
self::KeyRes => array_filter([
self::KeyTest => self::Value,
self::KeyInstance => $instance,
]),
];
}

/**
* @return array<string, array<array<string, string>>>
*/
private static function expectedCollection(?string $instance): array
{
return [
self::KeyRes => [
array_filter([
self::KeyTest => self::Value,
self::KeyInstance => $instance,
]),
],
];
}
}
47 changes: 38 additions & 9 deletions tests/Unit/Testing/PHPUnit/LaraStrictResourceTestCaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,38 @@ public function test(Closure $assert): void
$assert($this);
}

public function testResourceArray(): void
{
$resource = $this->createResource(new TestEntity(value: 'test'));

$this->assertEquals(
expected: self::expected(value: 'test', instance: '1'),
actual: $this->resourceArray(resource: $resource, container: $this->createContainer(instance: '1'))
);
}

public function testResourceArrayCollection(): void
{
$resource = LaraStrictResource::collection([new TestEntity(value: 'test')]);

$this->assertEquals(
expected: [self::expected(value: 'test', instance: '1')],
actual: $this->resourceArray(resource: $resource, container: $this->createContainer(instance: '1'))
);
}

public function testResourceArrayNull(): void
{
$this->assertNull($this->resourceArray(resource: null, container: $this->createContainer(instance: '1')));
$this->assertNull($this->resourceArray(resource: null));
}

protected function myAssert(string $value, string $instance): void
{
$this->assert(
object: new TestEntity(value: $value),
expected: [
'test' => $value,
'instance' => $instance,
],
container: new TestingContainer(
makeAlwaysBinding: static fn () => new TestAction($instance)
),
expected: self::expected($value, $instance),
container: $this->createContainer($instance),
);
}

Expand All @@ -57,10 +78,18 @@ protected function createResource(mixed $object): JsonResource
return new LaraStrictResource($object);
}

protected static function createContainer(string $value): TestingContainer
protected static function createContainer(string $instance): TestingContainer
{
return new TestingContainer(
makeAlwaysBinding: static fn () => new TestAction($value)
makeAlwaysBinding: static fn () => new TestAction($instance)
);
}

protected static function expected(string $value, string $instance): array
{
return [
'test' => $value,
'instance' => $instance,
];
}
}

0 comments on commit 34af3f0

Please sign in to comment.