Skip to content

Commit

Permalink
feat(Testing): Add ability to create request without Laravel container
Browse files Browse the repository at this point in the history
BREAKING CHANGE: createPostRequest has been renamed to createAndValidadeRequest
  • Loading branch information
pionl committed Jul 24, 2023
1 parent ab64cfa commit ecbd60b
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 12 deletions.
65 changes: 61 additions & 4 deletions src/Testing/Concerns/CreateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@

namespace LaraStrict\Testing\Concerns;

use Illuminate\Foundation\Application;
use Closure;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\UrlGenerator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Routing\UrlGenerator;
use LaraStrict\Testing\Laravel\Contracts\Validation\ValidatorMock;
use LaraStrict\Testing\Laravel\TestingContainer;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;

trait CreateRequest
{
/**
* Creates a form request using Laravel container (validation is triggered).
*
* @template TRequest of Request
*
* @param class-string<TRequest> $requestClass
Expand All @@ -22,7 +28,7 @@ trait CreateRequest
*
* @return TRequest
*/
public function createPostRequest(
public function createAndValidateRequest(
Application $application,
string $requestClass,
array $data,
Expand All @@ -31,8 +37,8 @@ public function createPostRequest(
array $files = [],
array $server = []
): object {
/** @var UrlGenerator $urlGenerator */
$urlGenerator = $application->make(UrlGenerator::class);
assert($urlGenerator instanceof UrlGenerator);

$uri = $urlGenerator->to('');
$symfonyRequest = SymfonyRequest::create(
Expand All @@ -53,4 +59,55 @@ public function createPostRequest(

return $application->make($requestClass);
}

/**
* Creates a form request without Laravel container (no validation is triggered - acts as validated).
*
* @template T of FormRequest
*
* @param class-string<T> $requestClass
* @param array<string, string|int|float|bool> $cookies
* @param array<string, array<UploadedFile>|UploadedFile> $files
* @param array<string, string|int|float|bool> $server
* @param array<string, object|Closure(array $makeBindings, class-string $abstract):(object|null)|null> $makeBindings A map of closures that will create.
* Receives make $parameters and
* $abstract string
* @return T
*/
protected function createFormRequest(
string $requestClass,
array $data,
string $method = 'POST',
string $accept = 'application/json',
array $cookies = [],
array $files = [],
array $server = [],
array $makeBindings = []
): Request {
$symfonyRequest = SymfonyRequest::create(
uri: 'https://testing',
method: $method,
parameters: $data,
cookies: $cookies,
files: $files,
server: [
...$server,
'HTTP_ACCEPT' => $accept,
],
);

$requestClass = $requestClass::createFromBase($symfonyRequest);

$requestClass->setContainer(
new TestingContainer(
makeBindings: $makeBindings,
makeAlwaysBinding: static fn (array $parameters, string $class) => new $class(...$parameters)
)
);

$requestClass->setValidator(new ValidatorMock($data));
$requestClass->validateResolved();

return $requestClass;
}
}
2 changes: 1 addition & 1 deletion src/Testing/Concerns/TestData.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
use Closure;

/**
* Create simple test case with data set and their assert closure
* Create simple test case with data set and their assert closure TODO: deprecate
*/
trait TestData
{
Expand Down
56 changes: 56 additions & 0 deletions src/Testing/Laravel/Contracts/Validation/ValidatorMock.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace LaraStrict\Testing\Laravel\Contracts\Validation;

use Illuminate\Contracts\Validation\Validator;
use Illuminate\Support\MessageBag;

class ValidatorMock implements Validator
{
public function __construct(
private readonly array $validatedData
) {
}

public function validate()
{
return $this->validatedData;
}

public function validated()
{
return $this->validatedData;
}

public function fails(): bool
{
return false;
}

public function failed()
{
return [];
}

public function sometimes($attribute, $rules, callable $callback)
{
return $this;
}

public function after($callback)
{
return $this;
}

public function errors()
{
return new MessageBag();
}

public function getMessageBag()
{
return new MessageBag();
}
}
4 changes: 2 additions & 2 deletions src/Testing/Laravel/TestingContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@
class TestingContainer implements Container
{
/**
* @param array<string, object|Closure(array):(object|null)|null> $makeBindings A map of closures that will create.
* @param array<string, object|Closure(array $makeBindings, class-string $abstract):(object|null)|null> $makeBindings A map of closures that will create.
* Receives make $parameters and
* $abstract string
* @param Closure(array,string):(object|null)|object|null $makeAlwaysBinding If makeBindings has no entry, it
* will call make on this closure or
* given object. Receives make
* $parameters and $abstract string
* @param Closure(Closure $call,array $parameters,?string $defaultMethod):mixed $call
* @param Closure(Closure $call,array $makeBindings,?string $defaultMethod):mixed $call
*/
public function __construct(
private array $makeBindings = [],
Expand Down
4 changes: 2 additions & 2 deletions tests/Feature/Testing/Concerns/CreateRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public function testSuccess(): void
$data = [
TestRequest::KeyTest => 'value',
];
$request = $this->createPostRequest($this->app(), requestClass: TestRequest::class, data: $data);
$request = $this->createAndValidateRequest($this->app(), requestClass: TestRequest::class, data: $data);
$this->assertEquals($data, $request->validated());
$this->assertTrue($request->acceptsJson());
$this->assertEquals('https://testing', $request->url());
Expand All @@ -25,6 +25,6 @@ public function testSuccess(): void
public function testFail(): void
{
$this->expectExceptionMessage('The test field is required.');
$this->createPostRequest($this->app(), TestRequest::class, []);
$this->createAndValidateRequest($this->app(), TestRequest::class, []);
}
}
4 changes: 1 addition & 3 deletions tests/Feature/User/Http/Middlewares/AuthenticateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use LaraStrict\User\Http\Middlewares\Authenticate;
use Tests\LaraStrict\Feature\TestCase;
use Tests\LaraStrict\Feature\Testing\Concerns\TestRequest;
use const true;

class AuthenticateTest extends TestCase
{
Expand Down Expand Up @@ -65,8 +64,7 @@ public function assert(
$this->app()
->detectEnvironment(static fn () => $environment->value);

$request = $this->createPostRequest(
application: $this->app(),
$request = $this->createFormRequest(
requestClass: TestRequest::class,
data: [
'test' => 'test',
Expand Down
13 changes: 13 additions & 0 deletions tests/Unit/Testing/Concerns/AutoAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Testing\Concerns;

class AutoAction
{
public function __construct(
public readonly string $test,
) {
}
}
45 changes: 45 additions & 0 deletions tests/Unit/Testing/Concerns/CreateRequestTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Testing\Concerns;

use LaraStrict\Testing\Concerns\CreateRequest;
use PHPUnit\Framework\TestCase;
use Tests\LaraStrict\Feature\Testing\Concerns\TestRequest;

class CreateRequestTest extends TestCase
{
use CreateRequest;

public function testNoContainerUsage(): void
{
$data = [
TestRequest::KeyTest => 'value',
];
$request = $this->createFormRequest(requestClass: TestRequest::class, data: $data);
$this->assertEquals($data, $request->validated());
$this->assertTrue($request->acceptsJson());
$this->assertEquals('https://testing', $request->url());
}

public function testContainerUsage(): void
{
$data = [
TestRequest::KeyTest => 'value',
];
$customAction = new CustomAction();
$request = $this->createFormRequest(
requestClass: TestContainerRequest::class,
data: $data,
makeBindings: [
CustomAction::class => $customAction,
]
);
$this->assertEquals($data, $request->validated());
$this->assertTrue($request->acceptsJson());
$this->assertEquals('https://testing', $request->url());
$this->assertNotNull($customAction->autoAction);
$this->assertEquals('test', $customAction->autoAction->test);
}
}
10 changes: 10 additions & 0 deletions tests/Unit/Testing/Concerns/CustomAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Testing\Concerns;

class CustomAction
{
public ?AutoAction $autoAction = null;
}
30 changes: 30 additions & 0 deletions tests/Unit/Testing/Concerns/TestContainerRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Tests\LaraStrict\Unit\Testing\Concerns;

use Illuminate\Foundation\Http\FormRequest;

class TestContainerRequest extends FormRequest
{
final public const KeyTest = 'test';

public function rules(): array
{
return [
self::KeyTest => ['required'],
];
}

protected function passedValidation()
{
$autoAction = $this->container->make(AutoAction::class, ['test']);
assert($autoAction instanceof AutoAction);

$customAction = $this->container->make(CustomAction::class);
assert($customAction instanceof CustomAction);

$customAction->autoAction = $autoAction;
}
}

0 comments on commit ecbd60b

Please sign in to comment.