Feature Test در Laravel چیست؟
Feature Test یکی از روشهای تست خودکار در Laravel است که به کمک آن میتوانیم عملکرد یک قابلیت از برنامه را بررسی کنیم. برای مثال، میتوانیم تست کنیم که یک صفحه بهدرستی باز میشود، یک API پاسخ JSON معتبر برمیگرداند، کاربر بدون ورود به حساب کاربری به صفحه خصوصی دسترسی ندارد یا اطلاعات یک فرم با موفقیت در دیتابیس ذخیره میشوند.
در Unit Test معمولاً یک متد یا بخش کوچک از کد را بهصورت مستقل بررسی میکنیم؛ اما در Feature Test میتوانیم چند بخش از برنامه را در کنار یکدیگر آزمایش کنیم.
فرض کنید در حال توسعه یک فروشگاه اینترنتی با Laravel هستید. برای قابلیت ثبت سفارش، ممکن است بخواهید بررسی کنید که:
- درخواست معتبر با پاسخ مناسب مواجه میشود.
- اطلاعات سفارش بهدرستی ذخیره میشوند.
- درخواست نامعتبر خطای اعتبارسنجی دریافت میکند.
- کاربر بدون مجوز نمیتواند سفارش ثبت کند.
- در صورت بروز خطا، برنامه رفتار مورد انتظار را نشان میدهد.
تمام این موارد میتوانند موضوع تستهای Feature باشند.
در Laravel ابزارهای تست HTTP این امکان را فراهم میکنند که درخواستها را بدون ارسال درخواست واقعی از طریق شبکه، درون برنامه شبیهسازی کنیم و پاسخ آنها را بررسی کنیم.
تفاوت Feature Test و Unit Test چیست؟
هر دو روش برای افزایش کیفیت نرمافزار استفاده میشوند، اما هدف یکسانی ندارند.
| ویژگی | Feature Test | Unit Test |
|---|---|---|
| هدف | بررسی عملکرد یک قابلیت | بررسی یک واحد کوچک از کد |
| راهاندازی برنامه Laravel | معمولاً انجام میشود | معمولاً انجام نمیشود |
| تست درخواست HTTP | مناسب است | معمولاً هدف اصلی نیست |
| تست تعامل با دیتابیس | امکانپذیر است | در Unit Test استاندارد معمولاً انجام نمیشود |
| نمونه کاربرد | تست ثبتنام کاربر | تست محاسبه تخفیف |
در پروژههای واقعی معمولاً به هر دو نوع تست نیاز دارید. Feature Test رفتار قابلیتها را بررسی میکند و Unit Test برای اطمینان از صحت منطق مستقل برنامه مفید است.
پیشنیازهای نوشتن Feature Test
برای دنبال کردن مثالهای این مقاله بهتر است با مفاهیم زیر آشنا باشید:
- ساختار پروژه Laravel
- Route و Controller
- درخواستهای HTTP
- پاسخ JSON
- اعتبارسنجی دادهها
- مفاهیم مقدماتی PHPUnit
مثالهای این مقاله بر اساس Laravel 13 نوشته شدهاند. ساختار اصلی آنها در نسخههای جدید قبلی Laravel نیز مشابه است؛ بااینحال، اگر از نسخه دیگری استفاده میکنید، مستندات همان نسخه را بررسی کنید.
مرحله اول: ساخت فایل Feature Test
ابتدا ترمینال را در ریشه پروژه Laravel باز کنید؛ یعنی همان مسیری که فایل artisan در آن قرار دارد.
سپس دستور زیر را اجرا کنید:
php artisan make:test ProductTest
Laravel فایلی با مسیر زیر ایجاد میکند:
tests/Feature/ProductTest.php
بهصورت پیشفرض، دستور make:test یک تست Feature ایجاد میکند. اگر قصد ساخت Unit Test داشته باشید، باید گزینه --unit را به دستور اضافه کنید.
فایل ایجادشده معمولاً از کلاس پایه Tests\TestCase استفاده میکند. این کلاس امکانات لازم برای اجرای تستهای برنامه Laravel را در اختیار شما قرار میدهد.
مرحله دوم: نوشتن اولین Feature Test
برای شروع، یک مسیر ساده ایجاد میکنیم که وضعیت برنامه را به شکل JSON برمیگرداند.
در فایل routes/web.php کد زیر را قرار دهید:
use Illuminate\Support\Facades\Route;
Route::get('/test-status', function () {
return response()->json([
'status' => 'success',
]);
});
اکنون مسیر /test-status پاسخی شبیه نمونه زیر برمیگرداند:
{
"status": "success"
}
در ادامه میخواهیم بررسی کنیم که این مسیر واقعاً پاسخ مورد انتظار را تولید میکند.
فایل tests/Feature/ProductTest.php را باز کنید و محتوای آن را با کد زیر جایگزین کنید:
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ProductTest extends TestCase
{
public function test_status_endpoint_returns_success(): void
{
$response = $this->get('/test-status');
$response->assertOk();
$response->assertJson([
'status' => 'success',
]);
}
}
در این تست سه کار انجام میدهیم.
ارسال درخواست GET
$response = $this->get('/test-status');
با استفاده از متد get() یک درخواست HTTP شبیهسازیشده به مسیر موردنظر ارسال میکنیم.
در این روش نیازی به باز کردن مرورگر یا ارسال درخواست واقعی از طریق اینترنت نیست.
بررسی وضعیت پاسخ
$response->assertOk();
این دستور بررسی میکند که پاسخ دارای وضعیت HTTP برابر با ۲۰۰ باشد.
اگر مسیر پیدا نشود یا خطای دیگری رخ دهد، تست ممکن است شکست بخورد.
بررسی محتوای JSON
$response->assertJson([
'status' => 'success',
]);
در این مرحله بررسی میکنیم که پاسخ JSON شامل مقدار مورد انتظار باشد.
با این سه مرحله، یک تست کاربردی برای بررسی رفتار یک مسیر ایجاد کردهایم.
مرحله سوم: اجرای Feature Test
برای اجرای تستها دستور زیر را در ترمینال وارد کنید:
php artisan test
اگر میخواهید فقط تستهایی را اجرا کنید که نام کلاس ProductTest را دارند، میتوانید از دستور زیر استفاده کنید:
php artisan test --filter=ProductTest
برای اجرای تستهای پوشه Feature نیز میتوانید بنویسید:
php artisan test --testsuite=Feature
در صورت موفقیت، نتیجه اجرای تست بهصورت موفق نمایش داده میشود. اگر تست شکست بخورد، گزارش خطا کمک میکند متوجه شوید کدام انتظار برقرار نشده است.
دقت کنید که در این مثال نام کلاس تست ProductTest است، اما متدی که آزمایش میکنیم مربوط به وضعیت یک مسیر است. در یک پروژه واقعی بهتر است نام فایل و کلاس تست با مسئولیت آن هماهنگ باشد؛ برای مثال، StatusEndpointTest نام دقیقتری برای این مثال خواهد بود.
مرحله چهارم: تست کردن پاسخ API
یکی از کاربردهای مهم Feature Test، بررسی APIها است.
فرض کنید API فهرست محصولات شما از مسیر زیر در دسترس است:
GET /api/products
و پاسخ آن ساختاری مشابه نمونه زیر دارد:
{
"data": [
{
"id": 1,
"name": "Keyboard",
"price": 1500000
}
]
}
میتوانیم تستی بنویسیم که وضعیت پاسخ و ساختار JSON را بررسی کند.
ابتدا دستور زیر را اجرا کنید:
php artisan make:test ProductApiTest
سپس فایل tests/Feature/ProductApiTest.php را با کد زیر تکمیل کنید:
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ProductApiTest extends TestCase
{
public function test_products_api_returns_expected_structure(): void
{
$response = $this->getJson('/api/products');
$response->assertOk();
$response->assertJsonStructure([
'data' => [
'*' => [
'id',
'name',
'price',
],
],
]);
}
}
در این مثال از getJson() استفاده کردهایم تا درخواست برای دریافت پاسخ JSON تنظیم شود.
متد assertJsonStructure() ساختار مورد انتظار پاسخ را بررسی میکند. علامت * به این معنا است که ساختار مشخصشده باید برای هر عضو آرایه data برقرار باشد.
این تست فرض میکند API شما چنین ساختاری را برمیگرداند. اگر پاسخ واقعی API با این ساختار متفاوت باشد، باید تست را مطابق قرارداد واقعی API تنظیم کنید.
همچنین اگر فهرست محصولات خالی باشد، بخش مربوط به اعضای آرایه چیزی برای بررسی نخواهد داشت. اگر وجود حداقل یک محصول نیز برای سناریوی شما مهم است، باید داده آزمایشی ایجاد کنید و تعداد عناصر را هم بررسی کنید.
برای مثال:
$response->assertJsonCount(1, 'data');
این assertion زمانی مناسب است که انتظار داشته باشید پاسخ دقیقاً یک محصول در data داشته باشد.
مرحله پنجم: تست کردن درخواست POST و اعتبارسنجی
در بسیاری از APIها، درخواست POST برای ایجاد اطلاعات جدید استفاده میشود؛ برای مثال، ثبت محصول یا ایجاد سفارش.
فرض کنید API ثبت محصول باید فیلدهای name و price را دریافت کند و فیلد name الزامی باشد.
در این حالت میتوانیم بررسی کنیم که ارسال درخواست بدون name با خطای اعتبارسنجی مواجه میشود.
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ProductValidationTest extends TestCase
{
public function test_product_name_is_required(): void
{
$response = $this->postJson('/api/products', [
'price' => 1500000,
]);
$response->assertUnprocessable();
$response->assertJsonValidationErrors([
'name',
]);
}
}
در این مثال:
postJson()درخواست POST با دادههای JSON ارسال میکند.assertUnprocessable()وضعیت HTTP برابر با ۴۲۲ را بررسی میکند.assertJsonValidationErrors()بررسی میکند که برای فیلدnameخطای اعتبارسنجی وجود داشته باشد.
این تست تنها زمانی موفق میشود که مسیر موردنظر واقعاً اعتبارسنجی لازم را اجرا کند و خطا را مطابق انتظار برگرداند.
اگر API شما به احراز هویت نیاز داشته باشد، ممکن است درخواست پیش از رسیدن به اعتبارسنجی با پاسخ ۴۰۱ یا ۴۰۳ مواجه شود. در آن صورت باید شرایط تست را متناسب با رفتار مورد انتظار تنظیم کنید.
مرحله ششم: تست کردن احراز هویت و سطح دسترسی
یکی از مهمترین کاربردهای Feature Test، بررسی دسترسی کاربران به صفحات و APIهای خصوصی است.
فرض کنید مسیر زیر فقط برای کاربران واردشده به سیستم در دسترس باشد:
use Illuminate\Support\Facades\Route;
Route::middleware('auth')->get('/account', function () {
return response()->json([
'message' => 'Welcome to your account',
]);
});
اکنون میخواهیم بررسی کنیم که کاربر مهمان نمیتواند به این مسیر دسترسی پیدا کند.
<?php
namespace Tests\Feature;
use Tests\TestCase;
class AccountAccessTest extends TestCase
{
public function test_guest_cannot_access_account(): void
{
$response = $this->get('/account');
$response->assertRedirect('/login');
}
}
این تست فرض میکند middleware احراز هویت برای کاربران مهمان به مسیر /login هدایت میکند. اگر در پروژه شما مسیر ورود متفاوت باشد یا درخواست API بهجای ریدایرکت پاسخ ۴۰۱ بدهد، باید assertion را مطابق رفتار واقعی برنامه تغییر دهید.
تست دسترسی کاربر واردشده
در یک پروژه واقعی، معمولاً میخواهیم دسترسی کاربر احرازشده را نیز بررسی کنیم.
اگر مدل User در پروژه شما موجود باشد، میتوانید از actingAs() استفاده کنید:
<?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class AccountAccessTest extends TestCase
{
public function test_authenticated_user_can_access_account(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->get('/account');
$response->assertOk();
$response->assertJson([
'message' => 'Welcome to your account',
]);
}
}
این مثال فرض میکند مدل User، کارخانه ساخت کاربر و جدول موردنیاز در پروژه شما تنظیم شدهاند. اگر factory ندارید یا مدل به فیلدهای اجباری بیشتری نیاز دارد، باید آن را متناسب با ساختار پروژه تکمیل کنید.
نکته مهم این است که actingAs() برای شبیهسازی کاربر احرازشده در تست مفید است؛ اما بهتنهایی، فرایند واقعی ورود با رمز عبور، بازیابی رمز یا ارسال کد تأیید را آزمایش نمیکند. برای این فرایندها باید تستهای جداگانه بنویسید.
مرحله هفتم: تست کردن دیتابیس با Feature Test
در بسیاری از قابلیتها، بررسی پاسخ HTTP کافی نیست. برای مثال، هنگام ثبت محصول باید مطمئن شویم اطلاعات در دیتابیس ذخیره شدهاند.
برای تست دیتابیس در Laravel میتوانیم از RefreshDatabase و assertionهای دیتابیس استفاده کنیم.
فرض کنید مسیر POST /api/products محصول جدیدی ایجاد میکند و مدل Product دارای ستونهای name و price است.
نمونه تست:
<?php
namespace Tests\Feature;
use App\Models\Product;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ProductCreationTest extends TestCase
{
use RefreshDatabase;
public function test_product_is_created_successfully(): void
{
$response = $this->postJson('/api/products', [
'name' => 'Keyboard',
'price' => 1500000,
]);
$response->assertCreated();
$this->assertDatabaseHas('products', [
'name' => 'Keyboard',
'price' => 1500000,
]);
}
}
در این مثال انتظار داریم API پس از ایجاد موفق محصول، وضعیت HTTP برابر با ۲۰۱ برگرداند.
سپس assertDatabaseHas() بررسی میکند که رکورد مورد انتظار در جدول products وجود داشته باشد.
این مثال فرض میکند:
- مسیر API ایجاد محصول را انجام میدهد.
- اعتبارسنجی درخواست با دادههای ارسالشده سازگار است.
- مدل و جدول
productsوجود دارند. - دیتابیس تست بهدرستی پیکربندی شده است.
- پاسخ موفق API دارای وضعیت ۲۰۱ است.
اگر API شما پس از ایجاد رکورد وضعیت دیگری برمیگرداند، باید assertion را با قرارداد واقعی آن هماهنگ کنید.
RefreshDatabase چه کاری انجام میدهد؟
Trait زیر:
use RefreshDatabase;
به مدیریت وضعیت دیتابیس در طول تستها کمک میکند تا دادههای ایجادشده در تستها باعث تداخل با سایر تستها نشوند.
بااینحال، باید دیتابیس تست را بهدرستی پیکربندی کنید. هرگز تستهای دیتابیس را بدون بررسی تنظیمات به دیتابیس عملیاتی کاربران متصل نکنید.
مرحله هشتم: تست کردن پاسخهای خطا
تستهای خوب تنها مسیر موفق را بررسی نمیکنند. رفتار برنامه هنگام دریافت درخواست نامعتبر یا مواجهه با خطا نیز اهمیت دارد.
برای مثال، فرض کنید مسیر نمایش محصول باید در صورت نبودن شناسه موردنظر، پاسخ ۴۰۴ برگرداند.
میتوانید چنین تستی بنویسید:
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ProductNotFoundTest extends TestCase
{
public function test_missing_product_returns_404(): void
{
$response = $this->getJson('/api/products/999999');
$response->assertNotFound();
}
}
این تست فرض میکند شناسه انتخابشده در دیتابیس تست وجود ندارد و مسیر API در صورت پیدا نکردن محصول، پاسخ ۴۰۴ میدهد.
اگر در برنامه شما محصولی با آن شناسه وجود داشته باشد یا رفتار API متفاوت باشد، نتیجه تست نیز متفاوت خواهد بود. بهتر است در تستهای وابسته به دیتابیس، دادهها را بهصورت کنترلشده ایجاد کنید تا نتیجه قابل پیشبینی باشد.
مرحله نهم: تست کردن ریدایرکتها و صفحات Blade
Feature Test تنها برای APIها نیست. میتوانید صفحات وب و ریدایرکتها را نیز بررسی کنید.
برای مثال، اگر صفحه اصلی سایت باید با وضعیت HTTP برابر با ۲۰۰ نمایش داده شود، میتوانید بنویسید:
$response = $this->get('/');
$response->assertOk();
اگر میخواهید بررسی کنید که یک صفحه Blade محتوای مشخصی دارد، میتوانید از assertionهای مربوط به پاسخ استفاده کنید:
$response = $this->get('/');
$response->assertSee('Welcome');
البته این مثال فرض میکند صفحه اصلی واقعاً عبارت Welcome را نمایش میدهد.
برای تست ریدایرکت نیز میتوانید از متدهایی مانند assertRedirect() استفاده کنید:
$response->assertRedirect('/login');
این assertion زمانی مناسب است که انتظار دارید مسیر موردنظر کاربر را به صفحه ورود هدایت کند.
مرحله دهم: مدیریت دادههای تست و تنظیمات محیط
برای اینکه Feature Test قابل تکرار و قابل اعتماد باشد، باید شرایط اجرای آن کنترلشده باشد.
استفاده از محیط testing
Laravel هنگام اجرای تستها محیط testing را فعال میکند. در صورت نیاز میتوانید فایل .env.testing را در ریشه پروژه ایجاد کنید و تنظیمات اختصاصی تست را در آن قرار دهید.
برای مثال، میتوانید اتصال دیتابیس تست را در تنظیمات مناسب پروژه مشخص کنید.
پس از تغییر تنظیمات، در صورت فعال بودن configuration cache، ممکن است لازم باشد آن را پاک کنید:
php artisan config:clear
پیش از اجرای تستهای وابسته به دیتابیس، نام دیتابیس و اتصال فعال را بررسی کنید.
استفاده از Factory
اگر تست شما به کاربر، محصول یا سفارش نیاز دارد، استفاده از Model Factory معمولاً بهتر از وارد کردن دستی تعداد زیادی رکورد است.
برای مثال:
$user = User::factory()->create();
این روش زمانی کار میکند که مدل User و factory آن بهدرستی تنظیم شده باشند.
Factory کمک میکند دادههای آزمایشی بهشکل یکپارچه ساخته شوند و تستها به اطلاعات موجود در محیط واقعی وابسته نباشند.
خطاهای رایج در Feature Test
۱. تست با پاسخ مورد انتظار هماهنگ نیست
ممکن است تصور کنید یک API باید وضعیت ۲۰۰ برگرداند، درحالیکه قرارداد واقعی آن برای ایجاد رکورد وضعیت ۲۰۱ است.
قبل از نوشتن assertion، رفتار مورد انتظار مسیر را مشخص کنید.
۲. دیتابیس تست آماده نیست
اگر تست به مدلها یا جداول دیتابیس وابسته باشد، نبود migration یا تنظیم نادرست اتصال ممکن است باعث شکست تست شود.
تنظیمات دیتابیس تست را بررسی کنید و از دیتابیس عملیاتی استفاده نکنید.
۳. کاربر احرازشده را ایجاد نکردهاید
اگر مسیر به ورود کاربر نیاز داشته باشد و تست بدون احراز هویت اجرا شود، احتمال دارد بهجای پاسخ موفق، ریدایرکت یا خطای ۴۰۱ دریافت کنید.
برای تست مسیرهای خصوصی، وضعیت کاربر را متناسب با سناریو تنظیم کنید.
۴. تست به دادههای قبلی وابسته است
اگر تست تنها زمانی موفق شود که رکورد خاصی از قبل در دیتابیس وجود داشته باشد، ممکن است در محیط دیگر شکست بخورد.
دادههای لازم را در خود تست یا Factory ایجاد کنید.
۵. چند درخواست نامرتبط در یک تست ارسال میکنید
بهتر است هر متد تست روی یک سناریوی مشخص تمرکز داشته باشد. تستهای کوتاه و هدفمند معمولاً خواناتر و سادهتر برای عیبیابی هستند.
۶. فقط مسیر موفق را بررسی میکنید
برای قابلیتهای مهم، علاوه بر سناریوی موفق، ورودی نامعتبر، کاربر مهمان، دسترسی غیرمجاز و نبود داده را نیز بررسی کنید.
بهترین روشها برای نوشتن Feature Test
برای اینکه تستهای پروژه در طول زمان قابل نگهداری بمانند، نکات زیر را رعایت کنید:
- برای هر سناریو نامی واضح و معنادار انتخاب کنید.
- رفتار قابل مشاهده برنامه را بررسی کنید، نه صرفاً جزئیات داخلی پیادهسازی را.
- تستها را تا حد امکان مستقل از یکدیگر نگه دارید.
- دادههای آزمایشی را بهصورت کنترلشده بسازید.
- از دیتابیس اختصاصی تست استفاده کنید.
- وضعیت HTTP و ساختار پاسخ API را بررسی کنید.
- سناریوهای احراز هویت و مجوزها را فراموش نکنید.
- تستهای مهم را پیش از ادغام تغییرات و انتشار نسخه اجرا کنید.
پرسشهای متداول
Feature Test در Laravel چه کاربردی دارد؟
از Feature Test برای بررسی عملکرد قابلیتهای برنامه استفاده میشود؛ مانند درخواستهای HTTP، پاسخ API، اعتبارسنجی، احراز هویت و تعامل با دیتابیس.
چگونه یک Feature Test ایجاد کنیم؟
در ریشه پروژه Laravel دستور زیر را اجرا کنید:
php artisan make:test ProductTest
بهصورت پیشفرض، فایل تست در پوشه tests/Feature ایجاد میشود.
چگونه Feature Test را اجرا کنیم؟
برای اجرای همه تستها دستور زیر را اجرا کنید:
php artisan test
برای اجرای تستهای یک کلاس مشخص میتوانید از --filter استفاده کنید.
آیا Feature Test به دیتابیس نیاز دارد؟
خیر. میتوانید فقط پاسخ HTTP یا JSON را بررسی کنید. اگر رفتار مورد آزمایش به اطلاعات ذخیرهشده وابسته باشد، باید دیتابیس تست و دادههای موردنیاز را آماده کنید.
آیا Feature Test میتواند API را آزمایش کند؟
بله. متدهایی مانند getJson() و postJson() برای شبیهسازی درخواستهای JSON و بررسی پاسخ آنها در دسترس هستند.
آیا Feature Test جایگزین Unit Test میشود؟
خیر. Feature Test و Unit Test اهداف متفاوتی دارند. Feature Test رفتار یک قابلیت را بررسی میکند و Unit Test برای آزمایش بخشهای کوچک و مستقل کد مناسب است.
جمعبندی
Feature Test یکی از ابزارهای کاربردی Laravel برای بررسی عملکرد واقعی قابلیتهای برنامه است. با استفاده از آن میتوانید درخواستهای HTTP، پاسخ JSON، اعتبارسنجی، احراز هویت، ریدایرکتها و تعامل با دیتابیس را آزمایش کنید.
برای شروع، یک تست ساده برای مسیر برنامه بنویسید و سپس بهتدریج سناریوهای مهمتر را اضافه کنید. در پروژههای واقعی، بهویژه سیستمهای فروشگاهی، APIهای خصوصی و سامانههای مالی، تست رفتارهای موفق و ناموفق به افزایش اطمینان از عملکرد برنامه کمک میکند.
مقاله بعدی این کلاستر: آموزش Unit Test در Laravel؛ در آن مقاله بهصورت اختصاصی نحوه تست منطق مستقل برنامه، نوشتن assertionها و آزمایش کلاسها و متدها را بررسی خواهیم کرد.
منابع رسمی
- مستندات تستنویسی Laravel: https://laravel.com/docs/13.x/testing
- مستندات رسمی HTTP Tests: https://laravel.com/docs/13.x/http-tests
- مستندات رسمی Database Testing: https://laravel.com/docs/13.x/database-testing





