I am trying to unit test various custom FormRequest
inputs. I found solutions that:
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.
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?
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.
} 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.
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.
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']);
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)); } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With