jcergolj / laravel-form-request-assertions
用于单元测试Laravel表单请求类的包
v1.12
2024-09-19 07:57 UTC
Requires
- php: >=8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.2
- laravel/pint: ^1.15
README
原因
Colin DeCarlo在Laracon online 21上就单元测试Laravel表单请求类进行了演讲。如果您还没有看过他的演讲,我建议您观看。他更喜欢将表单请求作为一个单元来测试,而不是作为功能测试。我也喜欢这种做法。
他请求Freek Van der Herten将他的gist代码转换为包。虽然我不是Freek;然而,我接受了挑战,并自己完成了它。所以这个包只是Colin的gist的一个包装,并且我添加了Jason的包中的两个方法,用于断言控制器是否有表单请求。
安装
需要的PHP >=8.0
composer require --dev jcergolj/laravel-form-request-assertions
用法
控制器
<?php namespace App\Http\Controllers; use App\Http\Requests\CreatePostRequest; use Illuminate\Http\Request; class PostController extends Controller { public function store(CreatePostRequest $request) { // ... } }
web.php 路由
<?php use App\Http\Controllers\PostController; Route::post('posts', [PostController::class, 'store']);
请求
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class CreatePostRequest extends FormRequest { public function authorize() { return $this->user()->id === 1 && $this->post->id === 1; } function rules() { return ['email' => ['required', 'email']]; } }
将特性添加到单元测试中
在包安装后添加TestableFormRequest
特性
<?php namespace Tests\Unit; use Tests\TestCase; use Jcergolj\FormRequestAssertions\TestableFormRequest; class CreatePostRequestTest extends TestCase { use TestableFormRequest; // ... }
控制器是否有表单请求测试?
public function controller_has_form_request() { $this->assertActionUsesFormRequest(PostController::class, 'store', CreatePostRequest::class); }
或者
public function controller_has_form_request() { $this->post(route('users.store')); $this->assertContainsFormRequest(CreateUserRequest::class); }
测试失败的验证规则
public function email_is_required() { $this->createFormRequest(CreatePostRequest::class) ->validate(['email' => '']) ->assertFails(['email' => 'required']) ->assertHasMessage('The email field is required.', 'email'); $this->createFormRequest(CreatePostRequest::class) ->validate(['password' => 'short']) ->assertFails(['password' => App\Rules\PasswordRule::class]); //custom password rule class }
测试属性有规则
当处理更复杂的规则时,您可能需要将逻辑提取到专门的自定义规则类中。在这种情况下,您不想在RequestTest类中测试逻辑,而是想在专门的自定义规则测试类中测试。在这里,您只关心给定的属性是否有/包含自定义规则。
public function email_has_custom_rule_applied() { $this->createFormRequest(CreatePostRequest::class) ->validate() ->assertHasRule('email', new CustomRule); // here we don't validate the rule, but just make sure rule is applied }
测试断言规则子集未失败
在某些情况下,您可能不关心整个请求是否通过,只是那组验证规则没有失败。
public function test_email_is_not_required() { /** Validation rules: ['email' => 'email|nullable'] */ $this->createFormRequest(CreatePostRequest::class) ->validate([]) ->assertRulesWithoutFailures(['email' => 'required']); }
警告:这仅检查规则没有失败,它不检查规则是否最初实际应用!
测试表单请求
/** @test */ function test_post_author_is_authorized() { $author = User::factory()->make(['id' => 1]); $post = Post::factory()->make(['id' => 1]); $this->createFormRequest(CreatePostRequest::class) ->withParam('post', $post) ->actingAs($author) ->assertAuthorized(); }
测试数据准备
测试在FormRequest
的prepareForValidation
方法中数据是如何准备的。
/** @test */ function test_transforms_email_to_lowercase_before_validation() { $this->createFormRequest(CreatePostRequest::class) ->onPreparedData(['email' => 'TeSt@ExAmPlE.cOm'], function (array $preparedData) { $this->assertEquals('test@example.com', $preparedData['email']); }); }
扩展
如果您需要额外的/自定义的断言,您可以轻松扩展\Jcergolj\FormRequestAssertions\TestFormRequest
类。
- 创建一个新的类,例如:
\Tests\Support\TestFormRequest
,它扩展了\Jcergolj\FormRequestAssertions\TestFormRequest
类。namespace Tests\Support; class TestFormRequest extends \Jcergolj\FormRequestAssertions\TestFormRequest { public function assertSomethingImportant() { // your assertions on `$this->request` } }
- 创建一个新的特性,例如:
\Tests\Traits\TestableFormRequest
,它使用了\Jcergolj\FormRequestAssertions\TestableFormRequest
特性。 - 重写
\Jcergolj\FormRequestAssertions\TestableFormRequest::createNewTestFormRequest
方法,以便返回(1)中创建的类的实例。namespace Tests\Support; trait TestableFormRequest { use \Jcergolj\FormRequestAssertions\TestableFormRequest; protected function createNewTestFormRequest(FormRequest $request): TestFormRequest { return new \Tests\Support\TestFormRequest($request); } }
- 在您的测试类中使用您自定义的特性而不是
\Jcergolj\FormRequestAssertions\TestableFormRequest
。
可用方法
createFormRequest(string $requestClass, $headers = [])
assertRouteUsesFormRequest(string $routeName, string $formRequest)
assertActionUsesFormRequest(string $controller, string $method, string $form_request)
validate(array $data)
by(Authenticatable $user = null)
actingAs(Authenticatable $user = null)
withParams(array $params)
withParam(string $param, $value)
assertAuthorized()
assertNotAuthorized()
assertPasses()
assertFails($expectedFailedRules = [])
assertHasMessage($message, $rule = null)
getFailedRules()
贡献者
非常感谢Colin和Jason。我从Colin的gist创建了一个包,并从Jason的包中复制了两个方法。