تست نویسی در لاراول با استفاده از PHPUnit
صرفنظر از اینکه شما با چه اپلیکیشنی کار میکنید، موضوع testing یک جنبه مهم است که اغلب نادیده گرفته میشود. امروز ما قصد داریم در در مورد تست نویسی در فریم ورک Laravel صحبت کنیم.Laravel به صورت پیش فرض PHPUnit را در هسته خورد دارد و از آن پشتیبانی می کند. PHPUnit یکی از محبوب ترین ابزار های testing در میان جامعِ برنامه نویسان php است. که به شما اجازه ی گرفتن تست های unit و functional را میدهد.
ما با معرفی unit and functional testing شروع می کنیم. همانطور که در این آموزش خواهید دید، چگونگی ایجاد unit and functional testing در لاروال را بررسی خواهیم کرد. ما فرض میکنیم که شما با اصول PHPUnit آشنا هستید، زیرا در این مقاله میخواهیم آن را برای لاراول بررسی کنیم.(در طول دوره آموزش جامع لاراول کدفرند تمامی مباحث را به صورت کامل یادخواهید گرفت).
تستِ واحد (unit testing) و تستِ فانکشنال (Functional Tests)
اگر قبلا با PHPUnit کار کرده باشید، باید بدانید که می توانید تست نویسی ها را به دو تست واحد و تست های عملکردی تقسیم کنید.در تست واحد، شما صحت یک تابع یا یک متُد خاص را آزمایش می کنید. مهمتر از همه، شما یک قطعه از کد خود را در یک زمان خاص آزمایش می کنید.در زمان توسعه، اگر متوجه شوید که متُدی که نوشتید شامل بیش از یک واحد منطق ( logic ) است، بهتر است که آن را به متُد های مختلف تقسیم کنید تا هر کدام یک قطعه کد منطقی و قابل تست داشته باشد.
بیایید یک نگاه سریع به این مثال داشته باشیم که یک نمونه ایده آل برای unit testing است.
public function getNameAttribute($value) { return ucfirst($value); }
همانطور که می بینید، این متُد یک کار و تنها یک کار دارد. و آن این است که با استفاده از تابع ucfirst حرف اول عنوان را به حرف بزرگ تبدیل میکند.
در حالی که unit test برای تست صحت یک قطعه منطقی کد استفاده می شود، از سوی دیگر functional test، به شما امکان می دهد تا صحت یک مورد خاص را آزمایش کنید. به طور خاص، به شما اجازه می دهد تا اقداماتی که کاربر در یک برنامه انجام می دهد شبیه سازی کنید.
به عنوان مثال، شما می توانید یک مورد functional test را برای برخی از قابلیت هایی که یک عمل لاگین دارد پیاده سازی کنید که ممکن است گام های زیر را شامل شود.
- برای دسترسی به صفحه لاگین به سیستم، یک درخواست از نوع GET ایجاد کنید.
- بررسی کنید که آیا ما در صفحه ورود به سیستم هستیم.
- یک درخواست POST را برای ارسال داده به صفحه لاگین ایجاد کنید.
- بررسی کنید که آیا session با موفقیت ایجاد شد یا نه ؟
حالا که کمی با این مفاهیم آشنا شدید. از بخش بعدی، نمونه هایی را می سازیم که نشان می دهد چگونه در لاراول میتوانیم unit and functional test بسازیم.
پیشنهاد شگفت انگیز کدفرند،دسترسی به تمامی دوره های آموزشی کدفرند تنها با ۶۹ هزار تومان
تنظیم پیش نیازها
قبل از اینکه ما شروع کنیم و تست های واقعی ایجاد کنیم، باید یک سری از چیزهایی را که در تست هایمان استفاده می شود، تنظیم کنیم.برای شروع ما مدل Post و migration مربوط به آن را ایجاد میکنیم. برای ایجاد مدل Post ، دستور زیر را اجرا کنید.
$php artisan make:model Post --migration
دستور بالا باید مُدلِ Post و database migration مرتبط را نیز ایجاد کند.کلاس مدل Post باید شبیه به کد زیر باشد:
<?php // app/Post.php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { // }
ما همچنین می خواهیم عنوان پست را ذخیره کنیم. اجازه دهید کد فایل پست migration database را بازنویسی کنیم.
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('posts'); } }
همانطور که می بینید، ستون $table->string(‘name’) را برای ذخیره عنوان پست اضافه کردیم. بعد، شما فقط باید فرمان migrate را اجرا کنید تا بتواند آن جدول را در پایگاه داده ایجاد کند.
$php artisan migrate
همچنین، اجازه دهید مدل Post را به صورت زیر بازنویسیک کنیم.
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { /** * Get the post title. * * @param string $value * @return string */ public function getNameAttribute($value) { return ucfirst($value); } }
ما فقط متُدِ accessor را اضافه کردیم که عنوان پست را اصلاح می کند و دقیقا همان چیزی است که ما در unit test مان آزمایش می کنیم.بعد، ما یک فایل controller را در مسیر app/Http/Controllers/AccessorController.php ایجاد میکنیم. این فایل برای بعدا که قصد ایجاد functional test را داریم نیاز خواهد شد.
<?php // app/Http/Controllers/AccessorController.php namespace App\Http\Controllers; use App\Post; use Illuminate\Http\Request; use App\Http\Controllers\Controller; class AccessorController extends Controller { public function index(Request $request) { // get the post-id from request params $post_id = $request->get("id", 0); // load the requested post $post = Post::find($post_id); // check the name property return $post->name; } }
در متد index، ما post id را از پارامترهای request بازیابی می کنیم و سعی می کنیم که شی post model را بارگذاری کنیم.اجازه دهید یک مسیر مرتبط را نیز درroutes/web.php اضافه کنیم.
Route::get('accessor/index', 'AccessorController@index');
تستِ واحد (unit testing)
در بخش قبلی ما راه اندازی اولیه را انجام دادیم که برای این قسمت و قسمت های بعدی مان مفید خواهد بود. در این بخش، ما قصد داریم که مفاهیم unit testing را در لاراول نشان دهیم.مثل همیشه لاراول یک artisan command برای ما فراهم کرده است.دستور زیر را برای ایجاد کلاس AccessorTest اجرا کنید.توجه داشته باشیم که ما کلید واژه ی –unit را ایجاد می کنیم که تست واحد را فولدر test/Unit ایجاد می کند.(در طول دوره آموزش جامع لاراول کدفرند تمامی مباحث را به صورت کامل یادخواهید گرفت)
php artisan make:test AccessorTest --unit
بعد یک کلاس با در مسیر tests/Unit/AccessorTest.php ایجاد میکنیم و کدهای زیر را داخل آن می نویسم:
<?php // tests/Unit/AccessorTest.php namespace Tests\Unit; use Tests\TestCase; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class AccessorTest extends TestCase { /** * A basic test example. * * @return void */ public function testExample() { $this->assertTrue(true); } }
اجازه دهید آن را با یک کد معنی دار جایگزین کنیم.
<?php // tests/Unit/AccessorTest.php namespace Tests\Unit; use Tests\TestCase; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\DB; use App\Post; class AccessorTest extends TestCase { /** * Test accessor method * * @return void */ public function testAccessorTest() { // load post manually first $db_post = DB::select('select * from posts where id = 1'); $db_post_title = ucfirst($db_post[0]->name); // load post using Eloquent $model_post = Post::find(1); $model_post_title = $model_post->name; $this->assertEquals($db_post_title, $model_post_title); } }
همانطور که می بینید، کد دقیقا همان است که در core PHP وجود دارد. ما فقط dependency مربوط به Laravel را وارد کردیم که به ما اجازه می دهد از API های مورد نیاز استفاده کنیم. در متد testAccessorTest، ما باید صحت متد getNameAttribute که در مدل Post وجود دارد را آزمایش کنیم.
برای انجام این کار، یک نمونه از پایگاه داده دریافت کرده ایم و خروجی مورد انتظار را در متغیر $db_post_title آماده کرده ایم. بعد، همان پست را با استفاده از مدل Eloquent بارگذاری می کنیم که متد getNameAttribute را نیز اجرا می کند تا عنوان پست را آماده کند. در نهایت، ما از متد assertEquals برای مقایسه دو متغیر به طور معمول استفاده می کنیم.
تستِ فانکشنال (Functional)
در این بخش، ما یک functional test case را ایجاد می کنیم که عملکرد Controller ای را که قبلا ایجاد کرده ایم، آزمایش می کند.دستور زیر را برای ایجاد کلاس AccessorTest اجرا کنید. مبینید که ما از کلمه کلیدی –unit استفاده نمی کنیم، به این صورت ما کلاسمان را در فولدرِ tests/feature میسازیم.
$php artisan make:test AccessorTest
سپس کدهای زیر را در کلاس tests/Feature/AccessorTest.php می نویسم:
<?php // tests/Feature/AccessorTest.php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; class AccessorTest extends TestCase { /** * A basic test example. * * @return void */ public function testExample() { $this->assertTrue(true); } }
اجازه دهید کدهای زیر را جایگزین کدهای بالا کنیم و به بررسی بخشهای مختلف آن بپردازیم:
<?php // tests/Feature/AccessorTest.php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\WithoutMiddleware; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Support\Facades\DB; class AccessorTest extends TestCase { /** * A basic test example. * * @return void */ public function testBasicTest() { // load post manually first $db_post = DB::select('select * from lvl_posts where id = 1'); $db_post_title = ucfirst($db_post[0]->name); $response = $this->get('/accessor/index?id=1'); $response->assertStatus(200); $response->assertSeeText($db_post_title); } }
کدی که نوشتیم باید برای کسانی که تجربه قبلی در functional testing دارند، آشنا باشد.
اول، ما نمونه ای از کلاس Post میگیریم و خروجی که انتظار داریم را در متغییر $db_post_title میریزیم. سپس، ما سعی میکنیم درخواست GET به آدرس /accessor/index?id=1 را شبیه سازی کنیم و پاسخ درخواستی که ایجاد کردیم را در متغییر $response میریزیم.
سپس، ما سعی می کنیم کد پاسخ را که در متغیر $response قرار دارد را با کد پاسخ مورد انتظارمان مطابقت دهیم. علاوه بر این، پاسخ باید حاوی یک عنوان باشد که با حروف بزرگ شروع می شود و این دقیقا همان چیزی است که ما با استفاده از متد assertSeeText آن را مطابقت می دهیم.
و این یک نمونه از functional test case است. در حال حاضر، ما همه چیزهایی را داریم که می توانیم test هایمان را بر علیه آن اجرا کنیم. بیایید پیش برویم و دستور زیر را در root برنامه مان اجرا کنیم تا همه test ها را اجرا کند.
$phpunit
این دستور باید تمام test هایی را که برای برنامه تان نوشته اید را اجرا کند. شما باید یک خروجی استاندارد PHPUnit را که وضعیت تست ها و assertion ها را نشان می دهد را ببینید.
1 دیدگاه
ممنون عالی و کاملا کاربردی بود