arielenter/laravel-phpunit-test-validation-rules

断言方法,帮助测试在给定的URL或路由名称中是否实现了所需的验证规则。

1.0.3 2024-09-30 04:23 UTC

README

西班牙语

Laravel Phpunit验证规则测试包。

描述

一个要在TestCase的测试中使用的特质。它提供断言来检查给定的请求方法中,给定的URL或路由名称是否实现了给定的验证规则。其更有吸引力的功能之一是,可以在一个断言指令中测试多个验证规则。

如何工作

通过向给定的URL或路由名称提交提供的无效字段值示例,并使用已建立的请求方法断言返回预期的错误消息,来测试所需的验证规则。无需提供预期的错误。请参阅“断言代码概要”部分,简要了解代码是如何做到这一点的。

安装

composer require --dev arielenter/laravel-phpunit-test-validation-rules

用法

假设以下路由已设置

web.php

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Validation\Rules\Password;

Route::patch('/patch', function (Request $request) {
    $request->validateWithBag('patch_error_bag',
            ['accept_field' => 'required']);
});

Route::delete('/delete', function (Request $request) {
    $request->validate(['user_id_field' => 'numeric|max:100']);
})->name('delete_route');

Route::post('/post', function (Request $request) {
    $regexRule = 'regex:/^[a-z]([a-z0-9]|[a-z0-9]\.[a-z0-9])*$/i';
    $request->validate([
        'username_field' => ['required', 'string', 'max:20', $regexRule],
        'password_field' => [Password::min(6)],
        'same_max_field' => 'max:20',
        'same_regex_field' => [$regexRule],
    ]);
});

/**
 * Unique extended requests for each route with defined rule and errorBag 
 * properties could have also been used as well. I just felt this was fine as 
 * a quick simple example.
 * 
 */

需要以下测试以确保所有所需的验证都已设置

RoutesValidationTest.php

<?php

namespace Tests\Feature;

use Arielenter\Validation\Assertions as ValidationAssertions;
use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Password;

class RoutesValidationTest extends TestCase {

    use ValidationAssertions;

    public function test_single_validation_rule_in_patch_url() {
        $this->assertValidationRuleIsImplementedInUrl('/patch',
                'accept_field', '', 'required', 'patch', 'patch_error_bag');
//         arguments: $url, $fieldName, $invalidValueExample, 
//         $validationRule, $requestMethod = 'post', $errorBag = 'default'
    }

    public function
    test_all_rules_exhaustively_one_rule_at_a_time_in_delete_url_and_route() {
        $this->assertValidationRuleIsImplementedInUrl('/delete',
                'user_id_field', 'not numeric', 'numeric', 'delete');

        $this->assertValidationRuleIsImplementedInRouteName('delete_route',
                'user_id_field', '101', ['numeric', 'max:100'], 'delete');
//      'numeric|max:100' could also had been used here
    }

    public function test_all_rules_exhaustively_in_post_url_all_at_once() {
        $file = UploadedFile::fake()->image('avatar.jpg');
        $tooLong = Str::repeat('x', 21);
        $this->assertValidationRulesAreImplementedInUrl(
                '/post',
                [
                    ['username_field', '', 'required'],
                    ['username_field', $file, 'string'],
                    [['username_field', 'same_max_field'], $tooLong, 'max:20'],
                    [
                        ['username_field', 'same_regex_field'],
                        ['0invalid', 'inva..lid', 'invalid.', 'inv@lid'],
/**
 *                      regex has to be nested inside an array bacause it 
 *                      contains a pipe | on it, otherwise it will be confuse 
 *                      as a composed string rule (example 'numeric|max:100')                      
*/
                        ['regex:/^[a-z]([a-z0-9]|[a-z0-9]\.[a-z0-9])*$/i']
                    ],
                    ['password_field', 'short', Password::min(6)]
                ]
        );
//      assertValidationRulesAreImplementedInRouteName is also available
    }
}

'list'参数数组形状

尽管我相信测试多个规则在一个断言中的最后一个例子已经说得很多了,但我还是决定包含一个PHPDoc来解释'list'参数的格式

    /**
     * @param array<array> $list List of arrays where validation rules are 
     * paired with invalid data examples for them. This nested arrays must have 
     * the following 3 keys: 0 for Field(s), 1 for Invalid Value Example(s) and 
     * lastly 2 for the Validation Rule desired to be tested. Key 0 and 1 can 
     * have multiple field names and invalid value examples respectively by 
     * nesting them inside an array. Field names must always be string values.
     * Composed validation rules can be given either as a pipe | delimited 
     * string (example 'numeric|max:100') or an array (example 
     * ['numeric', 'max:100']). Rules can only be string values or instances
     * of Illuminate\Contracts\Validation\Rule. Array shape:
     * array<array{
     *      0: string|array<string>,
     *      1: mixed|array<mixed|array<mixed>>,
     *      2: string|Rule|array<string|Rule>
     * }>
     * 
     */

断言代码概要

简要来说,以下函数用于获取失败的验证消息

validator($data, $rule)->messages()->first();

一旦知道了失败的验证错误消息,就可以使用它来检查在将无效数据提交到给定的URL时,是否返回了该消息,使用已存在的TestCase请求函数,如下所示,并使用其断言之一

$this->post($uri, $data)->assertSessionHasErrorsIn($errorBag, $keys);

以下是一个快速示例代码,简要说明了如何进行断言。

AssertionsCodeInANutshellTest.php

<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Validation\Rule;
use function validator;

class AssertionsCodeInANutshellTest extends TestCase {

    public function validationAssertionsInANutshell(
            string $url,
            string $fieldName,
            mixed $invalidValueExample,
            string|Rule|array $validationRule,
            string $requestMethod = 'post',
            string $errorBag = 'default'
    ): void {
        $fieldValue = [$fieldName => $invalidValueExample];
        $fieldRule = [$fieldName => $validationRule];

        $expectedErrorMsg = validator($fieldValue, $fieldRule)->messages()
                ->first();

        $fieldError = [$fieldName => $expectedErrorMsg];

        $this->$requestMethod($url, $fieldValue)
                ->assertSessionHasErrorsIn($errorBag, $fieldError);
    }

    public function test_assertions_code_in_a_nutshell(): void {
        $this->validationAssertionsInANutshell('/patch', 'accept_field', '',
                'required', 'patch', 'patch_error_bag');
    }
}