Persian (پارسی) translation by Erfan (you can also view the original English article)
صرفنظر از اینکه شما با چه اپلیکیشنی کار میکنید، موضوع testing یک جنبه مهم است که اغلب نادیده گرفته میشود. امروز ما قصد داریم در در مورد 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 است.
1 |
public function getNameAttribute($value) |
2 |
{
|
3 |
return ucfirst($value); |
4 |
}
|
همانطور که می بینید، این متُد یک کار و تنها یک کار دارد. و آن این است که با استفاده از تابع ucfirst
حرف اول عنوان را به حرف بزرگ تبدیل میکند.
در حالی که unit test برای تست صحت یک قطعه منطقی کد استفاده می شود، از سوی دیگر functional test، به شما امکان می دهد تا صحت یک مورد خاص را آزمایش کنید. به طور خاص، به شما اجازه می دهد تا اقداماتی که کاربر در یک برنامه انجام می دهد شبیه سازی کنید.
به عنوان مثال، شما می توانید یک مورد functional test را برای برخی از قابلیت هایی که یک عمل لاگین دارد پیاده سازی کنید که ممکن است گام های زیر را شامل شود.
- برای دسترسی به صفحه لاگین به سیستم، یک درخواست از نوع GET ایجاد کنید.
- بررسی کنید که آیا ما در صفحه ورود به سیستم هستیم.
- یک درخواست POST را برای ارسال داده به صفحه لاگین ایجاد کنید.
- بررسی کنید که آیا session با موفقیت ایجاد شد یا نه ؟
حالا که کمی با این مفاهیم آشنا شدید. از بخش بعدی، نمونه هایی را می سازیم که نشان می دهد چگونه در لاراول میتوانیم unit and functional test بسازیم.
تنظیم پیش نیازها
قبل از اینکه ما شروع کنیم و تست های واقعی ایجاد کنیم، باید یک سری از چیزهایی را که در تست هایمان استفاده می شود، تنظیم کنیم.
برای شروع ما مدل Post و migration مربوط به آن را ایجاد میکنیم. برای ایجاد مدل Post
، دستور زیر را اجرا کنید.
1 |
$php artisan make:model Post --migration |
دستور بالا باید مُدلِ Post
و database migration مرتبط را نیز ایجاد کند.
کلاس مدل Post
باید شبیه به کد زیر باشد:
1 |
<?php
|
2 |
// app/Post.php
|
3 |
namespace App; |
4 |
|
5 |
use Illuminate\Database\Eloquent\Model; |
6 |
|
7 |
class Post extends Model |
8 |
{
|
9 |
//
|
10 |
}
|
And the database migration file should be created at database/migrations/YYYY_MM_DD_HHMMSS_create_posts_table.php.
ما همچنین می خواهیم عنوان پست را ذخیره کنیم. اجازه دهید کد فایل migration database پست
را بازنویسی کنیم.
1 |
<?php
|
2 |
|
3 |
use Illuminate\Support\Facades\Schema; |
4 |
use Illuminate\Database\Schema\Blueprint; |
5 |
use Illuminate\Database\Migrations\Migration; |
6 |
|
7 |
class CreatePostsTable extends Migration |
8 |
{
|
9 |
/**
|
10 |
* Run the migrations.
|
11 |
*
|
12 |
* @return void
|
13 |
*/
|
14 |
public function up() |
15 |
{
|
16 |
Schema::create('posts', function (Blueprint $table) { |
17 |
$table->increments('id'); |
18 |
$table->string('name'); |
19 |
$table->timestamps(); |
20 |
});
|
21 |
}
|
22 |
|
23 |
/**
|
24 |
* Reverse the migrations.
|
25 |
*
|
26 |
* @return void
|
27 |
*/
|
28 |
public function down() |
29 |
{
|
30 |
Schema::dropIfExists('posts'); |
31 |
}
|
32 |
}
|
همانطور که می بینید، ستون $table->string('name')
را برای ذخیره عنوان پست اضافه کردیم. بعد، شما فقط باید فرمان migrate را اجرا کنید تا بتواند آن جدول را در پایگاه داده ایجاد کند.
1 |
$php artisan migrate
|
همچنین، اجازه دهید مدل Post
را به صورت زیر بازنویسی کینم.
1 |
<?php
|
2 |
namespace App; |
3 |
|
4 |
use Illuminate\Database\Eloquent\Model; |
5 |
|
6 |
class Post extends Model |
7 |
{
|
8 |
/**
|
9 |
* Get the post title.
|
10 |
*
|
11 |
* @param string $value
|
12 |
* @return string
|
13 |
*/
|
14 |
public function getNameAttribute($value) |
15 |
{
|
16 |
return ucfirst($value); |
17 |
}
|
18 |
}
|
ما فقط متُدِ accessor
را اضافه کردیم که عنوان پست را اصلاح می کند و دقیقا همان چیزی است که ما در unit test مان آزمایش می کنیم.
بعد، ما یک فایل controller را در مسیر app/Http/Controllers/AccessorController.php
ایجاد میکنیم. این فایل برای بعدا که قصد ایجاد functional test را داشتیم مفید است.
1 |
<?php
|
2 |
// app/Http/Controllers/AccessorController.php
|
3 |
namespace App\Http\Controllers; |
4 |
|
5 |
use App\Post; |
6 |
use Illuminate\Http\Request; |
7 |
use App\Http\Controllers\Controller; |
8 |
|
9 |
|
10 |
class AccessorController extends Controller |
11 |
{
|
12 |
public function index(Request $request) |
13 |
{
|
14 |
// get the post-id from request params
|
15 |
$post_id = $request->get("id", 0); |
16 |
|
17 |
// load the requested post
|
18 |
$post = Post::find($post_id); |
19 |
|
20 |
// check the name property
|
21 |
return $post->name; |
22 |
}
|
23 |
}
|
در متد index
، ما post id را از پارامترهای request بازیابی می کنیم و سعی می کنیم که شی post model را بارگذاری کنیم.
اجازه دهید یک مسیر مرتبط را نیز درroutes/web.php
اضافه کنیم.
1 |
Route::get('accessor/index', 'AccessorController@index'); |
And with that in place, you can run the http://your-laravel-site.com/accessor/index URL to see if it works as expected.
تستِ واحد (unit testing)
در بخش قبلی ما راه اندازی اولیه را انجام دادیم که برای این قسمت و قسمت های بعدی مان مفید خواهد بود. در این بخش، ما قصد داریم که مفاهیم unit testing را در لاراول نشان دهیم.
مثل همیشه لاراول یک artisan command برای ما فراهم کرده است.
دستور زیر را برای ایجاد کلاس AccessorTest
اجرا کنید.توجه داشته باشیم که ما کلید واژه ی --unit
را ایجاد می کنیم که تست واحد را فولدر test/Unit
ایجاد می کند.
1 |
$php artisan make:test AccessorTest --unit |
And that should create the following class at tests/Unit/AccessorTest.php
.
1 |
<?php
|
2 |
// tests/Unit/AccessorTest.php
|
3 |
namespace Tests\Unit; |
4 |
|
5 |
use Tests\TestCase; |
6 |
use Illuminate\Foundation\Testing\DatabaseMigrations; |
7 |
use Illuminate\Foundation\Testing\DatabaseTransactions; |
8 |
|
9 |
class AccessorTest extends TestCase |
10 |
{
|
11 |
/**
|
12 |
* A basic test example.
|
13 |
*
|
14 |
* @return void
|
15 |
*/
|
16 |
public function testExample() |
17 |
{
|
18 |
$this->assertTrue(true); |
19 |
}
|
20 |
}
|
اجازه دهید آن را با یک کد معنی دار جایگزین کنیم.
1 |
<?php
|
2 |
// tests/Unit/AccessorTest.php
|
3 |
namespace Tests\Unit; |
4 |
|
5 |
use Tests\TestCase; |
6 |
use Illuminate\Foundation\Testing\DatabaseMigrations; |
7 |
use Illuminate\Foundation\Testing\DatabaseTransactions; |
8 |
use Illuminate\Support\Facades\DB; |
9 |
use App\Post; |
10 |
|
11 |
class AccessorTest extends TestCase |
12 |
{
|
13 |
/**
|
14 |
* Test accessor method
|
15 |
*
|
16 |
* @return void
|
17 |
*/
|
18 |
public function testAccessorTest() |
19 |
{
|
20 |
// load post manually first
|
21 |
$db_post = DB::select('select * from posts where id = 1'); |
22 |
$db_post_title = ucfirst($db_post[0]->name); |
23 |
|
24 |
// load post using Eloquent
|
25 |
$model_post = Post::find(1); |
26 |
$model_post_title = $model_post->name; |
27 |
|
28 |
$this->assertEquals($db_post_title, $model_post_title); |
29 |
}
|
30 |
}
|
همانطور که می بینید، کد دقیقا همان است که در core PHP وجود دارد. ما فقط dependency مربوط به Laravel را وارد کردیم که به ما اجازه می دهد از API های مورد نیاز استفاده کنیم. در متد testAccessorTest
، ما باید صحت متد getNameAttribute
که در مدل Post
وجود دارد را آزمایش کنیم.
برای انجام این کار، یک نمونه از پایگاه داده دریافت کرده ایم و خروجی مورد انتظار را در متغیر $db_post_title
آماده کرده ایم. بعد، همان پست را با استفاده از مدل Eloquent بارگذاری می کنیم که متد getNameAttribute
را نیز اجرا می کند تا عنوان پست را آماده کند. در نهایت، ما از متد assertEquals
برای مقایسه دو متغیر به طور معمول استفاده می کنیم.
So that's how to prepare unit test cases in Laravel.
تستِ فانکشنال
در این بخش، ما یک functional test case را ایجاد می کنیم که عملکرد Controller ای را که قبلا ایجاد کرده ایم، آزمایش می کند.
دستور زیر را برای ایجاد کلاس AccessorTest
اجرا کنید. مبینید که ما از کلمه کلیدی --unit
استفاده نمی کنیم، به این صورت ما کلاسمان را در فولدرِ tests/feature
میسازیم.
1 |
$php artisan make:test AccessorTest
|
It'll create the following class at tests/Feature/AccessorTest.php
.
1 |
<?php
|
2 |
// tests/Feature/AccessorTest.php
|
3 |
namespace Tests\Feature; |
4 |
|
5 |
use Tests\TestCase; |
6 |
use Illuminate\Foundation\Testing\WithoutMiddleware; |
7 |
use Illuminate\Foundation\Testing\DatabaseMigrations; |
8 |
use Illuminate\Foundation\Testing\DatabaseTransactions; |
9 |
|
10 |
class AccessorTest extends TestCase |
11 |
{
|
12 |
/**
|
13 |
* A basic test example.
|
14 |
*
|
15 |
* @return void
|
16 |
*/
|
17 |
public function testExample() |
18 |
{
|
19 |
$this->assertTrue(true); |
20 |
}
|
21 |
}
|
Let's replace it with the following code.
1 |
<?php
|
2 |
// tests/Feature/AccessorTest.php
|
3 |
namespace Tests\Feature; |
4 |
|
5 |
use Tests\TestCase; |
6 |
use Illuminate\Foundation\Testing\WithoutMiddleware; |
7 |
use Illuminate\Foundation\Testing\DatabaseMigrations; |
8 |
use Illuminate\Foundation\Testing\DatabaseTransactions; |
9 |
use Illuminate\Support\Facades\DB; |
10 |
|
11 |
class AccessorTest extends TestCase |
12 |
{
|
13 |
/**
|
14 |
* A basic test example.
|
15 |
*
|
16 |
* @return void
|
17 |
*/
|
18 |
public function testBasicTest() |
19 |
{
|
20 |
// load post manually first
|
21 |
$db_post = DB::select('select * from lvl_posts where id = 1'); |
22 |
$db_post_title = ucfirst($db_post[0]->name); |
23 |
|
24 |
$response = $this->get('/accessor/index?id=1'); |
25 |
|
26 |
$response->assertStatus(200); |
27 |
$response->assertSeeText($db_post_title); |
28 |
}
|
29 |
}
|
کدی که نوشتیم باید برای کسانی که تجربه قبلی در functional testing دارند، آشنا باشد.
اول، ما نمونه ای از کلاس Post میگیریم و خروجی که انتظار داریم را در متغییر $db_post_title
میریزیم. سپس، ما سعی میکنیم درخواست GET به آدرس /accessor/index?id=1
را شبیه سازی کنیم و پاسخ درخواستی که ایجاد کردیم را در متغییر $response
میریزیم.
سپس، ما سعی مینیم کد پاسخ را که در متغیر $response
قرار دارد را با کد پاسخ مورد انتظارمان مطابقت دهیم. علاوه بر این، پاسخ باید حاوی یک عنوان باشد که با حروف بزرگ شروع می شود و این دقیقا همان چیزی است که ما با استفاده از متد assertSeeText
آن را مطابقت می دهیم.
و این یک نمونه از functional test case است. در حال حاضر، ما همه چیزهایی را داریم که می توانیم test هایمان را بر علیه آن اجرا کنیم. بیایید پیش برویم و دستور زیر را در root برنامه مان اجرا کنیم تا همه test ها را اجرا کند.
1 |
$phpunit
|
این دستور باید تمام test هایی را که برای برنامه تان نوشته اید را اجرا کند. شما باید یک خروجی استاندارد PHPUnit را که وضعیت تست ها و assertion ها را نشان می دهد را ببینید.
And with that, we're at the end of this article.
نتیجه گیری
امروزه ما جزئیات testing را در Laravel را بررسی کردیم که به صورت پیشفرض PHPUnit را در هسته خود پشتیبانی می کند. این مقاله با مقدمه ای برای unit و functional test آغاز شد و هرچه جلوتر رفتیم، جزئیات خاصی را در زمینه لاروول بررسی کردیم.
در این مقاله، مثال هایی را ایجاد کردیم که نشان داد چگونه می توانید با استفاده از فرمان artisan نمونه های unit و functional test ایجاد کنید.
اگر شما تازه با Laravel شروع به کار کرده اید یا به دنبال گسترش دانش یا سایت تان هستید، ما چیزهایی را که می توانید در Envato Market مطالعه کنید را در اختیار شما قرار می دهیم.
Don't hesitate to express your thoughts using the feed below!