Unit Test Laravel FormRequest

Я пытаюсь провести модульное тестирование различных пользовательских входов FormRequest. Я нашел решения, которые:

  1. Предложите использовать метод $this->call(…) и подтвердите response ожидаемым значением (ссылка на ответ). Это излишне, поскольку создает прямую зависимость от маршрутизации и контроллеров.

  2. Тест Тейлорса, из Laravel Framework, который можно найти в tests/Foundation/FoundationFormRequestTest.php. Там много издевательств и накладных расходов.

Я ищу решение, в котором я могу тестировать входные данные отдельных полей в соответствии с правилами (независимо от других полей в том же запросе).

Образец 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',
    ];
}

Желаемый тест:

public function testFirstNameField()
{
   // assertFalse, required
   // ...

   // assertTrue, required
   // ...

   // assertFalse, between
   // ...
}

public function testLastNameField()
{
    // ...
}

Как я могу выполнить модульное тестирование (утверждение) каждого правила проверки каждого поля изолированно и индивидуально?

Ответ 1

Я нашел хорошее решение на Laracast и добавил некоторые настройки в микс.

Код

public function setUp()
{
    parent::setUp();
    $this->rules     = (new UserStoreRequest())->rules();
    $this->validator = $this->app['validator'];
}

/** @test */
public function 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'));
}

protected function getFieldValidator($field, $value)
{
    return $this->validator->make(
        [$field => $value], 
        [$field => $this->rules[$field]]
    );
}

protected function validateField($field, $value)
{
    return $this->getFieldValidator($field, $value)->passes();
}

Обновить

Существует e2e подход к той же проблеме. Вы можете отправить данные, которые будут проверены, к рассматриваемому маршруту, а затем посмотреть, содержит ли ответ ошибки сеанса.

$response = $this->json('POST', 
    '/route_in_question', 
    ['first_name' => 'S']
);
$response->assertSessionHasErrors(['first_name');

Ответ 2

Друзья, пожалуйста, сделайте unit тест правильно, в конце концов, здесь тестируются не только rules, но и функции validationData и withValidator.

Вот как это должно быть сделано:

<?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));
    }


}