Laravel - Requests 說明及測試方法
Laravel 的 requests 可以將請求所需要的驗證進行分離,在這裡主要談一談 Requests 的建立方法 ,以及針對它進行測試的一些方式進行說明。
首先,建立一個 Requests
php artisan make:request TestRequest
接著開啟 App\Http\Requests\TestRequest.php
主要可以分為兩個區域, authorize及 rules
-
authorize 主要定義 request 請求需要的驗證
-
rules 主要定義 request 所要傳送的參數
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class TestRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'param1' => 'required|string|max:50',
'param2' => 'required|numeric',
];
}
建立 Model
php artisan make:model Models/Test -m
建構 migration 內容如下:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateTestsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tests', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('param1');
$table->double('param2');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('tests');
}
}
以及 model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Test extends Model
{
protected $fillable = ['param1', 'param2'];
}
建立 Controller
在開始前,先建立一個 resource
php artisan make:resource Test
接著建立 controller
php artisan make:controller TestController
在 controller 注入 TestRequest
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests\TestRequest;
use App\Models\Test;
use App\Http\Resources\Test as TestResource;
class TestController extends Controller
{
public function store(TestRequest $request)
{
$test = Test::create($request->validated());
return TestResource::make($test);
}
}
建立測試
php artisan make:test App/Http/Requests/TestRequestTest
接著開啟 tests/App/Http/Requests/TestRequestTest.php
測試 requests 的方式有幾種:
test_request_without_title
test_request_without_content
<?php
namespace Tests\Feature\App\Http\Requests;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\User;
use Illuminate\Http\Response;
class TestRequestTest extends TestCase
{
use RefreshDatabase, WithFaker;
/**
* setup factory
*
* @return void
*/
protected function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
/**
* A basic feature test example.
*
* @return void
*/
public function testExample()
{
$response = $this->get('/');
$response->assertStatus(200);
}
/**
* Test no param1
*
* @return void
*/
public function testRequestFailWithNoParam1()
{
$response = $this->actingAs($this->user)->postJson(route('test.store'), [
'param2' => $this->faker->numberBetween(1, 50)
]);
$response->assertStatus(
Response::HTTP_UNPROCESSABLE_ENTITY
);
$response->assertJsonValidationErrors('param1');
}
/**
* Test no param1 has more then 50 charts
*
* @return void
*/
public function testRequestFailWithParam1HasOver50Charts()
{
$response = $this->actingAs($this->user)->postJson(route('test.store'), [
'param1' => $this->faker->paragraph(),
]);
$response->assertStatus(
Response::HTTP_UNPROCESSABLE_ENTITY
);
$response->assertJsonValidationErrors('param2');
}
/**
* Test no param2
*
* @return void
*/
public function testRequestFailWithNoParam2()
{
$response = $this->actingAs($this->user)->postJson(route('test.store'), [
'param1' => $this->faker->word(),
]);
$response->assertStatus(
Response::HTTP_UNPROCESSABLE_ENTITY
);
$response->assertJsonValidationErrors('param2');
}
/**
* A basic feature test example.
*
* @return void
*/
public function testRequestPass()
{
$response = $this->actingAs($this->user)->postJson(route('test.store'), [
'param1' => $this->faker->word(),
'param2' => $this->faker->numberBetween(1, 50)
]);
$response->assertStatus(Response::HTTP_CREATED);
$response->assertJsonMissingValidationErrors([
'param1',
'param2'
]);
}
}
Test Provider
建立一個 provider 範例
說明如何更優雅的方式來測試 request
php artisan make:test App/Http/Requests/TestRequestTestProvider
內容建構
<?php
namespace Tests\Feature\App\Http\Requests;
use Tests\TestCase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\User;
use Illuminate\Http\Response;
use App\Http\Requests\TestRequest;
use Faker\Factory;
class TestRequestTestProvider extends TestCase
{
use RefreshDatabase;
public function setUp(): void
{
parent::setUp();
$this->validator = app()->get('validator');
$this->rules = (new TestRequest())->rules();
}
public function validationProvider()
{
/* WithFaker trait doesn't work in the dataProvider */
$faker = Factory::create(Factory::DEFAULT_LOCALE);
return [
'requestShouldFailWhenNoParam1IsProvided' => [
'passed' => false,
'data' => [
'param2' => $faker->numberBetween(1, 50)
]
],
'requestShouldFailWhenNoParam2IsProvided' => [
'passed' => false,
'data' => [
'param1' => $faker->word()
]
],
'requestShouldFailWhenParam1HasMoreThan50Characters' => [
'passed' => false,
'data' => [
'param1' => $faker->paragraph()
]
],
'requestShouldPassWhenDataIsProvided' => [
'passed' => true,
'data' => [
'param1' => $faker->word(),
'param2' => $faker->numberBetween(1, 50)
]
]
];
}
/**
* @test
* @dataProvider validationProvider
* @param bool $shouldPass
* @param array $mockedRequestData
*/
public function validationResultsAsExpected($shouldPass, $mockedRequestData)
{
$this->assertEquals(
$shouldPass,
$this->validate($mockedRequestData)
);
}
protected function validate($mockedRequestData)
{
return $this->validator
->make($mockedRequestData, $this->rules)
->passes();
}
}
問題排解
403 This action is unauthorized.
當發生 403 This action is unauthorized. 時,
表示需要有登入權限
ERR_TOO_MANY_REDIRECTS
請改用 post 及 user login 方式測試
執行測試需建立 $this->actingAs($this->user) 觸發行為
Error: Call to undefined function Tests\Feature\App\Http\Requests\assertStatus()
請檢查 $response->assertStatus 格式是否正確