Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Test Laravel's FormRequest

I am trying to unit test various custom FormRequest inputs. I found solutions that:

  1. Suggest using the $this->call(…) method and assert the response with the expected value (link to answer). This is overkill, because it creates a direct dependency on Routing and Controllers.

  2. Taylor’s test, from the Laravel Framework found in tests/Foundation/FoundationFormRequestTest.php. There is a lot of mocking and overhead done there.

I am looking for a solution where I can unit test individual field inputs against the rules (independent of other fields in the same request).

Sample FormRequest:

public function rules() {     return [         'first_name' => 'required|between:2,50|alpha',         'last_name'  => 'required|between:2,50|alpha',         'email'      => 'required|email|unique:users,email',         'username'   => 'required|between:6,50|alpha_num|unique:users,username',         'password'   => 'required|between:8,50|alpha_num|confirmed',     ]; } 

Desired Test:

public function testFirstNameField() {    // assertFalse, required    // ...     // assertTrue, required    // ...     // assertFalse, between    // ... }  public function testLastNameField() {     // ... } 

How can I unit test (assert) each validation rule of every field in isolation and individually?

like image 815
Dov Benyomin Sohacheski Avatar asked May 02 '16 08:05

Dov Benyomin Sohacheski


People also ask

What is unit testing in Laravel?

Unit tests are tests that focus on a very small, isolated portion of your code. In fact, most unit tests probably focus on a single method. Tests within your "Unit" test directory do not boot your Laravel application and therefore are unable to access your application's database or other framework services.

What is assertStatus in Laravel?

} The get method makes a GET request into the application, while the assertStatus method asserts that the returned response should have the given HTTP status code. In addition to this simple assertion, Laravel also contains a variety of assertions for inspecting the response headers, content, JSON structure, and more.

What is pest PHP?

Pest is a new testing PHP Framework developed by Nuno Maduro. While Pest itself is built on top of PHPUnit, the popular and widely adopted PHP testing framework, Pest aims to provide a better experience for writing tests. The philosophy is simple.


2 Answers

I found a good solution on Laracast and added some customization to the mix.

The Code

/**  * Test first_name validation rules  *   * @return void  */ public function test_valid_first_name() {     $this->assertTrue($this->validateField('first_name', 'jon'));     $this->assertTrue($this->validateField('first_name', 'jo'));     $this->assertFalse($this->validateField('first_name', 'j'));     $this->assertFalse($this->validateField('first_name', ''));     $this->assertFalse($this->validateField('first_name', '1'));     $this->assertFalse($this->validateField('first_name', 'jon1')); }  /**  * Check a field and value against validation rule  *   * @param string $field  * @param mixed $value  * @return bool  */ protected function validateField(string $field, $value): bool {     return $this->validator->make(         [$field => $value],         [$field => $this->rules[$field]]     )->passes(); }  /**  * Set up operations  *   * @return void  */ public function setUp(): void {     parent::setUp();      $this->rules     = (new UserStoreRequest())->rules();     $this->validator = $this->app['validator']; } 

Update

There is an e2e approach to the same problem. You can POST the data to be checked to the route in question and then see if the response contains session errors.

$response = $this->json('POST',      '/route_in_question',      ['first_name' => 'S'] ); $response->assertSessionHasErrors(['first_name']); 
like image 110
Dov Benyomin Sohacheski Avatar answered Oct 02 '22 13:10

Dov Benyomin Sohacheski


Friends, please, make the unit-test properly, after all, it is not only rules you are testing here, the validationData and withValidator functions may be there too.

This is how it should be done:

<?php  namespace Tests\Unit;  use App\Http\Requests\AddressesRequest; use App\Models\Country; use Faker\Factory as FakerFactory; use Illuminate\Routing\Redirector; use Illuminate\Validation\ValidationException; use Tests\TestCase; use function app; use function str_random;  class AddressesRequestTest extends TestCase {       public function test_AddressesRequest_empty()     {         try {             //app(AddressesRequest::class);             $request = new AddressesRequest([]);             $request                 ->setContainer(app())                 ->setRedirector(app(Redirector::class))                 ->validateResolved();         } catch (ValidationException $ex) {          }         //\Log::debug(print_r($ex->errors(), true));          $this->assertTrue(isset($ex));         $this->assertTrue(array_key_exists('the_address', $ex->errors()));         $this->assertTrue(array_key_exists('the_address.billing', $ex->errors()));     }       public function test_AddressesRequest_success_billing_only()     {         $faker = FakerFactory::create();         $param = [             'the_address' => [                 'billing' => [                     'zip'        => $faker->postcode,                     'phone'      => $faker->phoneNumber,                     'country_id' => $faker->numberBetween(1, Country::count()),                     'state'      => $faker->state,                     'state_code' => str_random(2),                     'city'       => $faker->city,                     'address'    => $faker->buildingNumber . ' ' . $faker->streetName,                     'suite'      => $faker->secondaryAddress,                 ]             ]         ];         try {             //app(AddressesRequest::class);             $request = new AddressesRequest($param);             $request                 ->setContainer(app())                 ->setRedirector(app(Redirector::class))                 ->validateResolved();         } catch (ValidationException $ex) {          }          $this->assertFalse(isset($ex));     }   } 
like image 43
Yevgeniy Afanasyev Avatar answered Oct 02 '22 12:10

Yevgeniy Afanasyev