├── .gitignore ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml ├── src ├── FormRequestTester.php └── TestsFormRequests.php └── tests ├── Stubs ├── Database │ ├── factories │ │ ├── PostFactory.php │ │ └── UserFactory.php │ └── migrations │ │ ├── 2014_10_12_000000_create_users_table.php │ │ └── 2019_01_24_182744_create_posts_table.php ├── FormRequests │ └── UpdatePost.php ├── Models │ ├── Post.php │ └── User.php └── ServiceProviders │ └── RouteServiceProvider.php ├── TestCase.php └── TestsFormRequestTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | _ide_helper.php 3 | .vscode 4 | .phpunit.result.cache 5 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Laravel FormRequest Tester 2 | 3 | A Simple collection of test helpers that help testing form request the easy way. 4 | 5 | ## Why Bother 6 | 7 | for full story on why this package was built please refer to [This Blog Post](https://mohammedmanssour.me/blog/testing-laravel-form-requests/) 8 | 9 | ## Installation 10 | 11 | 1. Using composer 12 | 13 | ``` 14 | composer require --dev mohammedmanssour/form-request-tester 15 | ``` 16 | 17 | 2. add `MohammedManssour\FormRequestTester\TestsFormRequests` trait to your test case. 18 | 19 | ## Testing a form request 20 | 21 | 1. you need to intialize the form request using `formRequest` method, it takes the FormRequest class as first argument and an array of request data as a second argument 22 | 23 | ```php 24 | $this->formRequest(UpdatePost::class, [ 25 | 'title' => 'New Title', 26 | 'content' => 'Some Content here' 27 | ]) 28 | ``` 29 | 30 | or you can use the intuitive methods to set form request method and data 31 | 32 | ```php 33 | $this->formRequest(UpdatePost::class) 34 | ->post([ 35 | 'title' => 'New Title', 36 | 'content' => 'Some Content here' 37 | ]) 38 | ``` 39 | 40 | the previous code will intialize the request with `post` method and `/fake-route` if you want to change these options you can via the options array that can be set as a third argument 41 | 42 | ```php 43 | $this->formRequest(UpdatePost::class, [ 44 | 'title' => 'New Title', 45 | 'content' => 'Some Content here' 46 | ], [ 47 | 'method' => 'put', 48 | 'route' => 'posts/{post}' 49 | ]) 50 | ``` 51 | 52 | ```php 53 | $this->formRequest(UpdatePost::class) 54 | ->put([ 55 | 'title' => 'New Title', 56 | 'content' => 'Some Content here' 57 | ]) 58 | ->withRoute('posts/{post}') 59 | ``` 60 | 61 | 2. use the available assertions to test for request 62 | 63 | ### Available Assertions 64 | 65 | | | | 66 | | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | 67 | | `$this->assertValidationPassed()` | To make sure the validation have passed successfully with the help of the provided data. | 68 | | `$this->assertValidationFailed()` | To make sure the validation have failed with the help of the provided data. | 69 | | `$this->assertValidationErrors($keysArray)` | To assert that the keys mentioned in the `$keysArray` have occurred in the errors bag. | 70 | | `$this->assertValidationErrorsMissing($keysArray)` | To assert that the keys mentioned in the `$keysArray` have not occurred in the errors bag. | 71 | | `$this->assertValidationMessages($messagesArray)` | To assert that the messages exists in the error bag. Used when you define custom messages for your validation. | 72 | | `$this->assertAuthorized()` | To assert that request have been authorized via the form request. | 73 | | `$this->assertNotAuthorized()` | To assert that request have not been authorized via the form request. | 74 | | `$this->assertValidationData($keysArray)` | To assert that the keys mentioned in the `$keysArray` have occurred in the validated data. | 75 | | `$this->assertValidationDataMissing($keysArray)` | To assert that the keys mentioned in the `$keysArray` have not occurred in the validated data. | 76 | 77 | ### Example Usage: 78 | 79 | Taking into consderation: 80 | 81 | 1. title & content are required field, 82 | 2. **Content field is required** is a custom error message used for content field 83 | 3. `$this->route` method is used in authorize method 84 | 4. `Route::put('posts/{post}', 'PostsController@update')` is the route used to update a post 85 | 86 | ```php 87 | $this->formRequest(UpdatePost::class,[ 88 | 'title' => 'New Title' 89 | ],[ 90 | 'method' => 'put' 91 | 'route' => 'posts/{post}' 92 | ])->assertAuthorized() 93 | ->assertValidationFailed() 94 | ->assertValidationErrors(['content']) 95 | ->assertValidationErrorsMissing(['title']) 96 | ->assertValidationMessages(['Content field is required']) 97 | ``` 98 | 99 | you can now use intuitive methods to build form request 100 | 101 | ```php 102 | $this->formRequest(UpdatePost::class) 103 | ->put([ 104 | 'title' => 'New Title' 105 | ]) 106 | ->withRoute('posts/1') 107 | ->assertAuthorized() 108 | ->assertValidationFailed() 109 | ->assertValidationErrors(['content']) 110 | ->assertValidationErrorsMissing(['title']) 111 | ->assertValidationMessages(['Content field is required']) 112 | ``` 113 | 114 | ## Form Requests related methods and how to work with them 115 | 116 | ### `$this->route('parameter')`: 117 | 118 | This method basically retreive the value of a routing rule parameter. 119 | 120 | For example if you have a routing rule `put posts/{post}` and browsed to `posts/1` then the value of `$this->route('post')` is **1**. 121 | for this to work with the package you need to 122 | 123 | 1. Register the routing rule in your application routing files `web.php` or `api.php` 124 | 125 | ```php 126 | Route::put('posts/{post}', [PostsController::class, 'update']); 127 | ``` 128 | 129 | 2. set the route using **FormRequestTester** `withRoute` method 130 | 131 | ```php 132 | $this->formRequest(UpdatePost::class) 133 | ->put($data) 134 | ->withRoute('posts/1'); 135 | ``` 136 | 137 | this why when you use the `$this->route('post')` in your form request, the result will be **1**. 138 | 139 | The package also supports substitube binding. All you need to be is to ؤreate an explicit binding and will do the work for you. 140 | 141 | ```php 142 | // somewhere in your app 🤔, ideally, your service provider 143 | Route::model('post', Post::class); 144 | ``` 145 | 146 | when you do the reigster, the value of `$this->route('post')` will be a **Post** model rather than the id. 147 | 148 | **Alternatively** 149 | 150 | if you don't want to register a route just for the sake of resolving a route parameter then you can use **FormRequestTester** `addRouteParameter($name, $value)`. 151 | 152 | ```php 153 | $this->formRequest(UpdatePost::class) 154 | ->put($data) 155 | ->addRouteParameter('post', 1) 156 | ->assertAuthorized(); 157 | ``` 158 | 159 | according to the example above, with the new method `addRouteParameter`, `$this->route('post')` will be resolved to **1** 160 | 161 | ### `$this->user()`: 162 | 163 | This method will reteive the current authenticated user. 164 | 165 | use laravel testing method `actingAs` to set a user as the current authenticated user 166 | 167 | ```php 168 | $user = User::factory()->create(); 169 | $this->actingAs($user); 170 | ``` 171 | 172 | ## Contributors: 173 | 174 | 1. [Mohammed Manssour](https://mohammedmanssour.me) 175 | 2. [Bryan Pedroza](https://www.bryanpedroza.com/) 176 | 3. [Kyler Moore](https://github.com/kylerdmoore) 177 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mohammedmanssour/form-request-tester", 3 | "version": "1.2.2", 4 | "description": "a collection of test helpers that help with testing form requests", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Mohammed Manssour", 9 | "email": "manssour.mohammed@gmail.com", 10 | "homepage": "https://mohammedmanssour.me" 11 | } 12 | ], 13 | "autoload": { 14 | "psr-4": { 15 | "MohammedManssour\\FormRequestTester\\": "src/" 16 | } 17 | }, 18 | "autoload-dev": { 19 | "psr-4": { 20 | "MohammedManssour\\FormRequestTester\\Tests\\": "tests/" 21 | } 22 | }, 23 | "require-dev": { 24 | "orchestra/testbench": "~6.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/FormRequestTester.php: -------------------------------------------------------------------------------- 1 | route('') method works out of the box 41 | * 42 | * @var string 43 | */ 44 | private $method; 45 | 46 | /** 47 | * form request route 48 | * used to make $this->route('') method works out of the box 49 | * 50 | * @var string 51 | */ 52 | private $route; 53 | 54 | /** 55 | * key/values pair of route paramters to be user with $this->route('parameter') name 56 | * 57 | * @var array 58 | */ 59 | private $routeParameters = []; 60 | 61 | /** 62 | * form request data 63 | * 64 | * @var array 65 | */ 66 | private $data; 67 | 68 | /** 69 | * validation errors 70 | * null when validation haven't been done yet 71 | * 72 | * @var array 73 | */ 74 | private $errors = null; 75 | 76 | /** 77 | * the form requests authorization status 78 | * 79 | * @var boolean 80 | */ 81 | private $formRequestAuthorized = true; 82 | 83 | /** 84 | * validated form request data 85 | * 86 | * @var array 87 | */ 88 | private $validated; 89 | 90 | /** 91 | * Create new FormRequestTester instance 92 | * 93 | * @param \Illuminate\Foundation\Testing\TestCase $test 94 | */ 95 | public function __construct($test) 96 | { 97 | $this->test = $test; 98 | } 99 | 100 | /*----------------------------------------------------- 101 | * Setters and getters 102 | -----------------------------------------------------*/ 103 | /** 104 | * set FormRequest Class 105 | * 106 | * @param string $formRequest 107 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 108 | */ 109 | public function setFormRequest($formRequest) 110 | { 111 | $this->formRequest = $formRequest; 112 | return $this; 113 | } 114 | 115 | /** 116 | * set FormRequest route 117 | * 118 | * @param string $route 119 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 120 | */ 121 | public function withRoute($route) 122 | { 123 | $this->route = $route; 124 | return $this; 125 | } 126 | 127 | /** 128 | * add route parameter to be resolved when using $this->route('parameter') 129 | * 130 | * @param string $name 131 | * @param mixed $value 132 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 133 | */ 134 | public function addRouteParameter($name, $value) 135 | { 136 | $this->routeParameters[$name] = $value; 137 | return $this; 138 | } 139 | 140 | /** 141 | * set Form request method and data 142 | * 143 | * @param string $method 144 | * @param array $data 145 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 146 | */ 147 | public function method($method, $data = []) 148 | { 149 | $this->method = $method; 150 | $this->data = $data; 151 | return $this; 152 | } 153 | 154 | /** 155 | * set form request method to get 156 | * 157 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 158 | */ 159 | public function get() 160 | { 161 | return $this->method('get', []); 162 | } 163 | 164 | /** 165 | * set form request method to post with data 166 | * 167 | * @param array $data 168 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 169 | */ 170 | public function post($data = []) 171 | { 172 | return $this->method('post', $data); 173 | } 174 | 175 | /** 176 | * set form request method to put with data 177 | * 178 | * @param array $data 179 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 180 | */ 181 | public function put($data = []) 182 | { 183 | return $this->method('put', $data); 184 | } 185 | 186 | /** 187 | * set form request method to delete with data 188 | * 189 | * @param array $data 190 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 191 | */ 192 | public function delete($data = []) 193 | { 194 | return $this->method('delete', $data); 195 | } 196 | 197 | /** 198 | * get current form requset 199 | * 200 | * @return \Illuminate\Foundation\Http\FormRequest 201 | */ 202 | public function formRequest() 203 | { 204 | $this->checkFormRequest(); 205 | return $this->currentFormRequest; 206 | } 207 | 208 | /*----------------------------------------------------- 209 | * Form Request specific methods 210 | -----------------------------------------------------*/ 211 | /** 212 | * check whether form request is build or not 213 | * 214 | * @return void 215 | */ 216 | public function checkFormRequest() 217 | { 218 | if (!is_null($this->currentFormRequest)) { 219 | return; 220 | } 221 | $this->buildFormRequest(); 222 | $this->validateFormRequest(); 223 | } 224 | 225 | /** 226 | * build form request 227 | * 228 | * @return void 229 | */ 230 | private function buildFormRequest() 231 | { 232 | $this->currentFormRequest = 233 | $this->formRequest::create($this->getRoute(), $this->method, $this->data) 234 | ->setContainer($this->test->getApp()) 235 | ->setRedirector($this->makeRequestRedirector()); 236 | 237 | $this->currentFormRequest->setRouteResolver(function () { 238 | $this->registerFakeRouteRule(); 239 | return $this->routeResolver(); 240 | }); 241 | 242 | $this->userResolver(); 243 | } 244 | 245 | /** 246 | * create fake request redirector to be used in request 247 | * 248 | * @return \Illuminate\Routing\Redirector 249 | */ 250 | private function makeRequestRedirector() 251 | { 252 | $fakeUrlGenerator = Mockery::mock(); 253 | $fakeUrlGenerator->shouldReceive('to', 'route', 'action', 'previous')->withAnyArgs()->andReturn(null); 254 | 255 | $redirector = Mockery::mock(Redirector::class); 256 | $redirector->shouldReceive('getUrlGenerator')->andReturn($fakeUrlGenerator); 257 | 258 | return $redirector; 259 | } 260 | 261 | /** 262 | * validates form request and save the errors 263 | * 264 | * @return void 265 | */ 266 | private function validateFormRequest() 267 | { 268 | try { 269 | $this->currentFormRequest->validateResolved(); 270 | $this->validated = $this->currentFormRequest->validated(); 271 | } catch (ValidationException $e) { 272 | $this->errors = $e->errors(); 273 | } catch (AuthorizationException $e) { 274 | $this->formRequestAuthorized = false; 275 | } 276 | } 277 | 278 | /** 279 | * register Fake Route to be 280 | * 281 | * @return void 282 | */ 283 | private function registerFakeRouteRule() 284 | { 285 | if (empty($this->routeParameters)) { 286 | return null; 287 | } 288 | 289 | $fakeRoute = collect($this->routeParameters) 290 | ->keys() 291 | ->map(fn ($param) => "{{$param}}") 292 | ->prepend('fake-route') 293 | ->implode('/'); 294 | 295 | Route::{$this->method}($fakeRoute); 296 | } 297 | 298 | private function getRoute() 299 | { 300 | if ($this->route) { 301 | return $this->route; 302 | } 303 | 304 | return collect($this->routeParameters) 305 | ->map(fn ($value) => $value) 306 | ->prepend('fake-route') 307 | ->implode('/'); 308 | } 309 | 310 | /** 311 | * find the routing rule that matches the route provided with withRoute 312 | * 313 | * @return void 314 | */ 315 | private function routeResolver() 316 | { 317 | $routes = Route::getRoutes(); 318 | try { 319 | $route = $routes->match($this->currentFormRequest); 320 | 321 | // Substitute Bindings 322 | $router = app()->make(Registrar::class); 323 | $router->substituteBindings($route); 324 | $router->substituteImplicitBindings($route); 325 | 326 | return $route; 327 | } catch (\Exception $e) { 328 | } 329 | 330 | return null; 331 | } 332 | 333 | /** 334 | * add the logic that formRequest needs to resolve user 335 | * 336 | * @return void 337 | */ 338 | public function userResolver() 339 | { 340 | $this->currentFormRequest->setUserResolver(function () { 341 | return auth()->user(); 342 | }); 343 | } 344 | 345 | /*---------------------------------------------------- 346 | * Assertions functions 347 | --------------------------------------------------- */ 348 | /** 349 | * assert form request validation have passed 350 | * 351 | * @return $this 352 | */ 353 | public function assertValidationPassed() 354 | { 355 | $this->checkFormRequest(); 356 | 357 | if (!$this->formRequestAuthorized) { 358 | Assert::fail('Form request is not authorized'); 359 | } 360 | 361 | if (!empty($this->errors)) { 362 | Assert::fail('Validation failed: ' . json_encode($this->errors)); 363 | return $this; 364 | } 365 | 366 | $this->succeed('Validation passed successfully'); 367 | return $this; 368 | } 369 | 370 | /** 371 | * assert form request validation have failed 372 | * 373 | * @return $this 374 | */ 375 | public function assertValidationFailed() 376 | { 377 | $this->checkFormRequest(); 378 | 379 | if (!$this->formRequestAuthorized) { 380 | Assert::fail('Form request is not authorized'); 381 | } 382 | 383 | if (!empty($this->errors)) { 384 | $this->succeed('Validation have failed'); 385 | return $this; 386 | } 387 | 388 | Assert::fail('Validation have passed'); 389 | return $this; 390 | } 391 | 392 | /** 393 | * assert the validation errors has the following keys 394 | * 395 | * @param array $keys 396 | * @return $this 397 | */ 398 | public function assertValidationErrors($keys) 399 | { 400 | $this->checkFormRequest(); 401 | 402 | if (!$this->formRequestAuthorized) { 403 | Assert::fail('Form request is not authorized'); 404 | } 405 | 406 | foreach (Arr::wrap($keys) as $key) { 407 | $this->test->assertTrue( 408 | isset($this->errors[$key]), 409 | "Failed to find a validation error for key: '{$key}'" 410 | ); 411 | } 412 | 413 | return $this; 414 | } 415 | 416 | 417 | /** 418 | * assert the validation errors doesn't have a key 419 | * 420 | * @param array $keys 421 | * @return $this 422 | */ 423 | public function assertValidationErrorsMissing($keys) 424 | { 425 | $this->checkFormRequest(); 426 | 427 | if (!$this->formRequestAuthorized) { 428 | Assert::fail('Form request is not authorized'); 429 | } 430 | 431 | foreach (Arr::wrap($keys) as $key) { 432 | $this->test->assertTrue( 433 | !isset($this->errors[$key]), 434 | "validation error for key: '{$key}' was found in errors array" 435 | ); 436 | } 437 | 438 | return $this; 439 | } 440 | 441 | /** 442 | * assert that validation has the messages 443 | * 444 | * @return $this 445 | */ 446 | public function assertValidationMessages($messages) 447 | { 448 | $this->checkFormRequest(); 449 | 450 | $errors = Arr::flatten(Arr::wrap($this->errors)); 451 | foreach ($messages as $message) { 452 | $this->test->assertContains( 453 | $message, 454 | $errors, 455 | "Failed to find the validation message '{$message}' in the validation messages" 456 | ); 457 | } 458 | 459 | return $this; 460 | } 461 | 462 | /** 463 | * assert that the current user was authorized by the form request 464 | * 465 | * @return $this 466 | */ 467 | public function assertAuthorized() 468 | { 469 | $this->checkFormRequest(); 470 | 471 | $this->test->assertTrue($this->formRequestAuthorized, "Form Request was not authorized"); 472 | return $this; 473 | } 474 | 475 | /** 476 | * assert that the current user was not authorized by the form request 477 | * 478 | * @return $this 479 | */ 480 | public function assertNotAuthorized() 481 | { 482 | $this->checkFormRequest(); 483 | 484 | $this->test->assertFalse($this->formRequestAuthorized, "Form Request was authorized"); 485 | return $this; 486 | } 487 | 488 | /** 489 | * assert the success of the current test 490 | * 491 | * @param string $message 492 | * @return void 493 | */ 494 | public function succeed($message = '') 495 | { 496 | $this->test->assertTrue(true, $message); 497 | } 498 | 499 | /** 500 | * assert the validation errors has the following keys 501 | * 502 | * @param array $keys 503 | * @return $this 504 | */ 505 | public function assertValidationData($keys) 506 | { 507 | $this->checkFormRequest(); 508 | 509 | if (!$this->formRequestAuthorized) { 510 | Assert::fail('Form request is not authorized'); 511 | } 512 | 513 | foreach (Arr::wrap($keys) as $key) { 514 | $this->test->assertTrue( 515 | Arr::has($this->validated, $key), 516 | "Failed to find a validation data for key: '{$key}'" 517 | ); 518 | } 519 | 520 | return $this; 521 | } 522 | 523 | /** 524 | * assert the validation data doesn't have a key 525 | * 526 | * @param array $keys 527 | * @return $this 528 | */ 529 | public function assertValidationDataMissing($keys) 530 | { 531 | $this->checkFormRequest(); 532 | 533 | if (!$this->formRequestAuthorized) { 534 | Assert::fail('Form request is not authorized'); 535 | } 536 | 537 | foreach (Arr::wrap($keys) as $key) { 538 | $this->test->assertTrue( 539 | !Arr::has($this->validated, $key), 540 | "validation error for key: '{$key}' was found in validated array" 541 | ); 542 | } 543 | 544 | return $this; 545 | } 546 | } 547 | -------------------------------------------------------------------------------- /src/TestsFormRequests.php: -------------------------------------------------------------------------------- 1 | 'Route to instantiate form request with', 'method' => 'to instantiate form request with'] 28 | * @return \MohammedManssour\FormRequestTester\FormRequestTester 29 | */ 30 | public function formRequest(string $formRequestType, $data = [], $options = []) 31 | { 32 | $options = array_merge([ 33 | 'method' => 'post', 34 | 'route' => null 35 | ], $options); 36 | 37 | return (new FormRequestTester($this)) 38 | ->setFormRequest($formRequestType) 39 | ->method($options['method'], $data) 40 | ->withRoute($options['route']); 41 | } 42 | 43 | public function getApp() 44 | { 45 | return $this->app; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/Stubs/Database/factories/PostFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->paragraph(), 27 | 'user_id' => User::factory() 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Stubs/Database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | $this->faker->name, 27 | 'email' => $this->faker->unique()->safeEmail, 28 | 'email_verified_at' => null, 29 | 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 30 | 'remember_token' => Str::random(10), 31 | ]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Stubs/Database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('email')->unique(); 20 | $table->timestamp('email_verified_at')->nullable(); 21 | $table->string('password'); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('users'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/Stubs/Database/migrations/2019_01_24_182744_create_posts_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->text('content'); 19 | $table->integer('user_id')->unsigned()->index(); 20 | $table->timestamps(); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('posts'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Stubs/FormRequests/UpdatePost.php: -------------------------------------------------------------------------------- 1 | ['required'], 22 | 'user_id' => ['required', 'exists:users,id'] 23 | ]; 24 | } 25 | 26 | public function messages() 27 | { 28 | return [ 29 | 'content.required' => 'Content Field is required', 30 | 'user_id.required' => 'User Field is required', 31 | 'user_id.exists' => 'User is not valid', 32 | ]; 33 | } 34 | 35 | public function authorize() 36 | { 37 | return $this->getModel()->user_id == $this->user()->id; 38 | } 39 | 40 | public function getModel() 41 | { 42 | if ($this->model) { 43 | return $this->model; 44 | } 45 | 46 | if (is_a($this->route('post'), Post::class)) { 47 | $this->model = $this->route('post'); 48 | return $this->model; 49 | } 50 | 51 | $this->model = Post::find($this->route('post')); 52 | throw_if(!$this->model, NotFoundHttpException::class); 53 | return $this->model; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/Stubs/Models/Post.php: -------------------------------------------------------------------------------- 1 | loadMigrationsFrom(__DIR__ . '/Stubs/Database/migrations'); 18 | 19 | // and other test setup steps you need to perform 20 | } 21 | 22 | protected function getPackageProviders($app) 23 | { 24 | return [RouteServiceProvider::class]; 25 | } 26 | 27 | /** 28 | * Define environment setup. 29 | * 30 | * @param \Illuminate\Foundation\Application $app 31 | * @return void 32 | */ 33 | protected function getEnvironmentSetUp($app) 34 | { 35 | // Setup default database to use sqlite :memory: 36 | $app['config']->set('database.default', 'testbench'); 37 | $app['config']->set('database.connections.testbench', [ 38 | 'driver' => 'sqlite', 39 | 'database' => ':memory:', 40 | 'prefix' => '', 41 | ]); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/TestsFormRequestTest.php: -------------------------------------------------------------------------------- 1 | user = User::factory()->create(); 23 | $this->post = Post::factory()->create([ 24 | 'user_id' => $this->user 25 | ]); 26 | 27 | $this->actingAs($this->user); 28 | } 29 | 30 | /** @test */ 31 | public function validation_will_pass() 32 | { 33 | $data = [ 34 | 'content' => 'This is content', 35 | 'user_id' => $this->user->id 36 | ]; 37 | 38 | $this->formRequest( 39 | UpdatePost::class, 40 | $data, 41 | ['method' => 'put', 'route' => "posts/{$this->post->id}"] 42 | )->assertValidationPassed(); 43 | 44 | // test the ability to build form request using the intuitive methods 45 | $this->formRequest(UpdatePost::class) 46 | ->put($data) 47 | ->withRoute("posts/{$this->post->id}") 48 | ->assertValidationPassed(); 49 | } 50 | 51 | /** @test */ 52 | public function validation_will_fail() 53 | { 54 | $this->formRequest( 55 | UpdatePost::class, 56 | [], 57 | ['method' => 'put', 'route' => "posts/{$this->post->id}"] 58 | ) 59 | ->assertValidationFailed() 60 | ->assertValidationErrors(['user_id', 'content']) 61 | ->assertValidationMessages(['Content Field is required', 'User Field is required']); 62 | 63 | // test the ability to build form request using the intuitive methods 64 | $this->formRequest(UpdatePost::class) 65 | ->put() 66 | ->withRoute("posts/{$this->post->id}") 67 | ->assertValidationFailed() 68 | ->assertValidationErrors(['user_id', 'content']) 69 | ->assertValidationMessages(['Content Field is required', 'User Field is required']); 70 | } 71 | 72 | /** @test */ 73 | public function validation_will_fail_because_user_id_is_not_valid() 74 | { 75 | $data = [ 76 | 'content' => 'This is content', 77 | 'user_id' => 2000 78 | ]; 79 | 80 | $this->formRequest( 81 | UpdatePost::class, 82 | $data, 83 | ['method' => 'put', 'route' => "posts/{$this->post->id}"] 84 | ) 85 | ->assertValidationFailed() 86 | ->assertValidationErrors(['user_id']) 87 | ->assertValidationErrorsMissing(['content']) 88 | ->assertValidationMessages(['User is not valid']); 89 | } 90 | 91 | /** @test */ 92 | public function form_request_will_authorize_request() 93 | { 94 | $this->formRequest( 95 | UpdatePost::class, 96 | [], 97 | ['method' => 'put', 'route' => "posts/{$this->post->id}"] 98 | )->assertAuthorized(); 99 | 100 | // test the ability to build form request using the intuitive methods 101 | $this->formRequest(UpdatePost::class) 102 | ->put() 103 | ->withRoute("posts/{$this->post->id}") 104 | ->assertAuthorized(); 105 | } 106 | 107 | /** @test */ 108 | public function form_request_will_not_authorize_request() 109 | { 110 | $user = User::factory()->create(); 111 | $this->actingAs($user); 112 | $this->formRequest( 113 | UpdatePost::class, 114 | [], 115 | ['method' => 'put', 'route' => "posts/{$this->post->id}"] 116 | )->assertNotAuthorized(); 117 | } 118 | 119 | /** @test */ 120 | public function form_request_will_assert_the_validated_data() 121 | { 122 | $this->formRequest(UpdatePost::class) 123 | ->put([ 124 | 'content' => 'Fake Content', 125 | 'user_id' => $this->user->id, 126 | ]) 127 | ->withRoute("posts/{$this->post->id}") 128 | ->assertValidationPassed() 129 | ->assertValidationData(['content', 'user_id']) 130 | ->assertValidationDataMissing(['not_available_key']); 131 | } 132 | 133 | /** @test */ 134 | public function substitube_binding_can_retreive_the_right_model() 135 | { 136 | Route::model('post', Post::class); 137 | 138 | $tester = $this->formRequest(UpdatePost::class) 139 | ->put([]) 140 | ->withRoute("posts/{$this->post->id}"); 141 | $tester->checkFormRequest(); 142 | 143 | $this->assertInstanceOf(Post::class, $tester->formRequest()->route('post')); 144 | } 145 | 146 | /** @test */ 147 | public function can_resolve_route_parameters_with_the_help_of_addRouteParameter_method() 148 | { 149 | $formRequestTester = $this->formRequest(UpdatePost::class) 150 | ->put([ 151 | 'content' => 'Fake Content', 152 | 'user_id' => $this->user->id 153 | ])->addRouteParameter('post', $this->post->id) 154 | ->assertAuthorized() 155 | ->assertValidationPassed(); 156 | 157 | $this->assertEquals(1, $formRequestTester->formRequest()->route('post')); 158 | } 159 | } 160 | --------------------------------------------------------------------------------