کانال بله, جهت پشتیبانی و اطلاع رسانی کانال بله, جهت پشتیبانی و اطلاع رسانی
عضویت

آموزش تست گیری در Laravel

  1. مقدمه
  2. تست گیری از اپلیکیشن
    • تعامل با اپلیکیشن
    • تست JSON API
    • Session / (سنجش هویت)Authentication
    • غیرفعال سازی Middleware
    • درخواست های HTTP اختصاصی
    • Assertion ها در ابزار PHPunit
  3. کار با بانک های اطلاعاتی
    • بازگردانی پایگاه داده پس از هر تست
    • Model factory ها
  4. Mocking (تقلید در تست کد)
    • Mock کردن event ها
    • Mock کردن job ها
    • Mock کردن facade ها

مقدمه

در طراحی Laravel امکان برای تست گیری و آزمون واحد تعبیه شده است. در واقع پشتیبانی از قابلیت تست گیری با ارائه ی PHPunit به صورت از پیش تنظیم شده و آماده در اختیار برنامه نویس قرار می گیرد، به طوری که یک فایل phpunit.xml ویژه ی اپلیکیشن شما برای تست برنامه در این چارچوب نرم افزاری تنظیم شده است. علاوه بر آن، Laravel تعداد زیادی تابع کمکی جهت تست آسان برنامه فراهم می کند.
فایل ExampleTest.php به عنوان یک نمونه تست در پوشه ی tests در اختیار شما قرار گرفته است.پس از نصب اپلیکیشن Laravel، کافی است دستور phpunit را برای راه اندازی تست های خود، در پنجره ی فرمان اجرا نمایید.

محیط Test

به هنگام اجرای تست، Laravel خود مقدار متغیر configuration environment را به صورت اتوماتیک بر روی testing تنظیم می کند. همچنین در زمان تست گیری، Laravel به صورت خودکار session و cache را بر روی درایور array تنظیم می کند. این بدان معنی است که داده هایsession و cache در طول اجرای تست به صورت ماندگار ذخیره نمی شوند.
البته این امکان را دارید تا هر تعداد variable configuration environment که برای برنامه ی لازم دارید، تنظیم نمایید. می توانید به متغیرهای (environment variable) testing در فایل phpunit.xml دسترسی داشته و آن ها را مطابق نیاز ویرایش نمایید. اما لازم است قبل از راه اندازی تست، دستور config:clear را برای پاک کردن محتویات configuration cache اجرا نمایید.

فرایند ایجاد و اجرای تست ها

برای ایجاد یک مورد تست (test case) جدید، کافی است دستور آرتیزان make:test را اجرا کنید:

                    php artisan make:test UserTest

این دستور یک کلاس UserTest جدید داخل پوشه ی tests پروژه ایجاد می کند. پس از ایجاد این کلاس می توانید متدهای تست گیری (test method) خود را همان طور که با استفاده از ابزار PHPUnit می نویسید، در این کلاس اعلان نمایید. حال برای راه اندازی تست های خود، دستورphpunit را در پنجره ی فرمان اجرا نمایید:

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class UserTest extends TestCase
{
   
   public function testExample()
   {
       $this->assertTrue(true);
   }
}
نکته:

اگر متد setUp اختصاصی خود را داخل کلاس های تست گیری (test class) تعریف کنید، در آن صورت لازم است دستور parent::setUp را نیز فراخوانی نمایید.

تست گیری از اپلیکیشن

فریم ورک لاراول با ارائه ی API های بسیار کارآمد (fluent) ارسال درخواست های HTTP به اپلیکیشن، تست خروجی و پر کردن فرم را آسان ساخته است. برای ایده گرفتن می توانید به فایل نمونه تست ExampleTest.php که در پوشه ی tests قرار دارد، مراجعه نمایید:

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       $this->visit('/')
            ->see('Laravel 5')
            ->dontSee('Rails');
   }
}

متد visit یک درخواست GET به اپلیکیشن شما ارسال می کند. متد see انتظار دارد که رشته ی متنی پاس داده شده، در پاسخ یا خروجی اپلیکیشن وجود دارد و می توانید آن را مشاهده نمایید (رشته ی Laravel 5 در خروجی اپلیکیشن وجود دارد). در مقابل متد dontSee انتظار دارد که متن ارائه شده در خروجی (پاسخ اپلیکیشن) موجود نباشد. این نمونه ساده ترین نوع تست اپلیکیشن در Laravel است.

نمونه های تست پیچیده تر –تعامل با اپلیکیشن

در زیر نگاهی به مثال های تست گیری از کلیک بر روی لینک و پر کردن فرم می اندازیم.

نوشتن تست برای کلیک بر روی لینک

در این تست یک درخواست به اپلیکیشن داده، طی آن یک لینک را (در پاسخ بازگشتی) کلیک می کنیم و سپس انتظار داریم که به آدرس URI دلخواه هدایت شویم. فرض کنید (در پاسخ) یک لینک با مقدار متنی "About Us" داریم:

                <a href="/about-us">About Us</a>

حال تستی طراحی می کنیم که لینک را کلیک کرده و در آن انتظار می رود که کاربر به صفحه ی مورد نظر ارجاع داده شود.

public function testBasicExample()
{
   $this->visit('/')
        ->click('About Us')
        ->seePageIs('/about-us');
}

نوشتن تست برای پر کردن فرم

Laravel همچنین توابع متعددی برای تست گرفتن از فرم ها فراهم می کند. متدهای type، select، check، attach و press به شما این امکان را می دهند تا با تمامی ورودی های فرم تعامل داشته و از آن ها تست بگیرید. فرض بگیرید چنین فرمی را در صفحه ی ثبت نام اپلیکیشن داریم:

<form action="/register" method="POST">
   {{ csrf_field() }}
                <div>
       Name: <input type="text" name="name">
   </div>
                <div>
                <input type="checkbox" value="yes" name="terms"> Accept Terms
   </div>
                <div>
                <input type="submit" value="Register">
   </div>
</form>

می توانیم یک تست نوشته که این فرم را تکمیل و سپس نتایج آن را بررسی کند:

public function testNewUserRegistration()
{
   $this->visit('/register')
        ->type('Taylor', 'name')
        ->check('terms')
        ->press('Register')
        ->seePageIs('/dashboard');
}

در صورتی که فرم شما فیلدهای ورودی دیگری نظیر radio button یا drop-down box داشته باشد، آن ها را نیز می توانید به راحتی پر کنید. در جدول زیر تمامی متدهای ویرایش فیلدهای فرم به همراه شرح کاربرد آورده شده است:

شرح
متد
متنی را وارد فیلد مورد نظر می کند.
$this->type($text, $elementName)
یک فیلد radio button یا drop-down را انتخاب می کند.
$this->select($value, $elementName)
فیلد checkbox مورد نظر را تیک دار می کند.
$this->check($elementName)
فیلد checkbox را از حالت تیک دار خارج می کند.
$this->uncheck($elementName)
یک فایل را به فرم پیوست می کند.
$this->attach($pathToFile,$elementName)
دکمه ای که دارای اسم یا متن معین هست را فشار می دهد.
$this->press($buttonTextOrElementName)

کار با فایل های پیوست (attachment)

چنانچه فرم شما دارای ورودی از نوع file باشد، در آن صورت می توانید فایل ها را با استفاده از متد attach به فرم پیوست نمایید:

public function testPhotoCanBeUploaded()
{
   $this->visit('/upload')
        ->type('File Name', 'name')
        ->attach($absolutePathToFile, 'photo')
        ->press('Upload')
        ->see('Upload Successful!');
}

تست گیری از API های JSON

Laravel همچنین تعداد زیادی تابع کمکی (helper) برای تست JSON API و پاسخ های آن فراهم می کند. به عنوان مثال، با توابع get، post،put، patch و delete می توان درخواست هایی را با متدهای درخواست فرم (http verb ها) به اپلیکیشن صادر کرد. علاوه بر آن می توانید اطلاعات و header به عنوان پارامتر به این متدها ارسال کنید. برای شروع یک تست طراحی می کنیم که با استفاده از POST درخواستی را به اکشن /userارسال کرده و انتظار دارد که آرایه ی ورودی (ارسال شده به عنوان آرگومان) را در خروجی با فرمت JSON دریافت کند:

<?php
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       $this->json('POST', '/user', ['name' => 'Sally'])
            ->seeJson([
                'created' => true,
            ]);
   }
}

متد seeJson آرایه ی ورودی را به فرمت JSON تبدیل می کند و سپس بررسی می کند (قطعه JSON مورد نظر name =>Sally) JSON fragment داخل پاسخ کل بازگشتی JSON، تولید شده توسط اپلیکیشن هست یا خیر. مادامی که قطعه داده ی JSON مورد نظر در خروجی باشد، این تست پاس می شود حتی اگر داخل خروجی property های دیگری (علاوه بر قطعه JSON مورد نظر) هم باشد.

مطابقت دقیق JSON با آرایه

اگر می خواهید بررسی کنید که آرایه ی ورودی با JSON خروجی اپلیکیشن کاملا منطبق باشد، در آن صورت بایستی متد seeJsonEquals را بکار ببرید:

<?php
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       $this->json('POST', '/user', ['name' => 'Sally'])
            ->seeJsonEquals([
                'created' => true,
            ]);
   }
}

بررسی انطباق دقیق ساختاری JSON

همچنین می توان طی یک مقایسه بررسی کرد که پاسخ JSON با ساختار مورد انتظار در خروجی منطبق باشد (JSON تولید شده توسط اپلیکیشن دارای ساختار معینی باشد). برای این منظور لازم است متد seeJsonStructure را به همراه لیستی از کلیدهای تودرتو به عنوان پارامتر فراخوانی کنید:

<?php
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       $this->get('/user/1')
            ->seeJsonStructure([
                'name',
                'pet' => [
                    'name', 'age'
                ]
            ]);
   }
}

در مثال بالا انتظار می رود یک name و یک شی تودرتو pet به همراه کلیدهای name و age خود دریافت گردد. اگر در خروجی کلیدهای اضافی هم وجود داشته باشد، متد seeJsonStructure با شکست مواجه نمی شود. بدین معنی که اگر شی pet یک attribute هم به نام weight داشته باشد، این تست باز هم پاس می شود.
می توانید با استفاده از کاراکتر * به عنوان آرگومان، انتظار داشته باشید که ساختار JSON خروجی یک لیست باشد که در آن هر آیتم حتما دربردارنده ی attribute های موجود در مجموعه مقادیر ورودی باشد. برای مثال انتظار داشت (می توان assert کرد) که هر کاربر در لیست دارای حداقل یک کلید (attribute) id، name و email باشد:

<?php
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       // Assert that each user in the list has at least an id, name and email attribute.
       $this->get('/users')
            ->seeJsonStructure([
                '*' => [
                    'id', 'name', 'email'
                ]
            ]);
   }
}

می توانید کاراکتر * را به صورت تودرتو نیز به عنوان آرگومان به تابع seeJsonStructure پاس دهید. در این مثال انتظار داریم که هر المان userدر JSON خروجی حاوی مجموعه ای از attribute ها باشد و هر شی pet موجود در هر user نیز باز خود دربردارنده ی مجموعه ای از attribute ها باشد:

$this->get('/users')
    ->seeJsonStructure([
        '*' => [
            'id', 'name', 'email', 'pets' => [
                '*' => [
                    'name', 'age'
                ]
            ]
        ]
    ]);

کار با Sessions / (سنجش هویت) Authentication

Laravel تعداد زیادی تابع کمکی برای کار با session فراهم می کند که می توان به هنگام تست گیری از آن ها استفاده کرد. ابتدا با فراخوانی متدwithSession داده های session را در آرایه ی ارائه شده قرار می دهیم. با این کار می توان به راحتی پیش از تست درخواست به اپلیکیشن، یکsession را با داده های آن بارگذاری نمود:

<?php
class ExampleTest extends TestCase
{
   public function testApplication()
   {
       $this->withSession(['foo' => 'bar'])
            ->visit('/');
   }
}

همان طور که می دانید یکی از موارد کاربرد session نگهداری اطلاعات کاربری (user state) همچون کاربر احراز هویت شده است. متد (helper) کمکی actingAs یک روش آسان برای احراز هویت کاربر ارائه شده فراهم می آورد. در زیر با استفاده از model factory یک کاربر جدید ایجاد کرده و سپس آن کاربر را سنجش هویت می کنیم:

<?php
class ExampleTest extends TestCase
{
   public function testApplication()
   {
       $user = factory(App\User::class)->create();
 
       $this->actingAs($user)
            ->withSession(['foo' => 'bar'])
            ->visit('/')
            ->see('Hello, '.$user->name);
   }
}

همچنین می توانید مشخص کنید از کدام guard برای احراز هویت کاربر ارائه شده استفاده شود. برای این منظور اسم guard مورد نظر را به عنوان آرگومان دوم به متد actingAs ارسال کنید:

                    $this->actingAs($user, 'backend')

غیرفعال سازی Middleware

به هنگام تست اپلیکیشن، بهتر است middleware را برای برخی از تست های خود غیرفعال نمایید. این کار به شما اجازه می دهد route ها وcontroller اپلیکیشن خود را به صورت مجزا و فارغ از هر middleware concern (دغدغه و نگرانی middleware) تست کنید. Laravel با ارائه ی یک trait ساده به نام WithoutMiddleware به شما اجازه می دهد تمامی middleware ها را به صورت خودکار برای کلاس تست گیری (test class) مورد نظر غیرفعال نمایید:

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
   use WithoutMiddleware;
   //
}

اگر می خواهید middleware را تنها برای تعدادی از متدهای تست گیری خود غیرفعال کنید، در آن صورت بایستی تابع withoutMiddleware را داخل بدنه ی متدهای تست گیری مد نظر فراخوانی نمایید:

<?php
class ExampleTest extends TestCase
{
   
   public function testBasicExample()
   {
       $this->withoutMiddleware();
       $this->visit('/')
            ->see('Laravel 5');
   }
}

درخواست های HTTP اختصاصی

اگر می خواهید به اپلیکیشن خود یک درخواست HTTP اختصاصی داده و در پاسخ نمونه (شی) کامل از Illuminate\Http\Response را دریافت نمایید، در آن صورت می توانید متد call را صدا بزنید:

public function testApplication()
{
   $response = $this->call('GET', '/');
   $this->assertEquals(200, $response->status());
}

اگر درخواست را با متدهای POST، PUT یا PATCH به اپلیکیشن بدهید، در آن صورت این امکان برای شما وجود دارد که آرایه ای از داده های ورودی را نیز همراه با request ارسال نمایید. این اطلاعات از طریق نمونه ی ایجاد شده از Request، در route و controller قابل دسترسی هستند:

                    $response = $this->call('POST', '/user', ['name' => 'Taylor']);

Assertion ها در ابزار PHPUnit

Laravel تعداد زیادی متد assertion اختصاصی دارد که می توان در تست های PHPUnit بکار برد:

شرح
متد
انتظار دارد که پاسخ از کلاینت دارای کد وضعیت OKباشد.
->assertResponseOk();
انتظار دارد پاسخ از کلاینت دارای کد وضعیت مشخص شده باشد.
->assertResponseStatus($code);
انتظار دارد view خروجی دارای بخشی از داده ی مرتبط (bind شده) باشد.
->assertViewHas($key, $value =null);
انتظار دارد view مورد نظر دارای لیستی از داده های پاس داده شده باشد.
->assertViewHasAll(array$bindings);
انتظار دارد view خروجی یک قطعه داده bind شده (ارسال شده به عنوان پارامتر) را نداشته باشد.
->assertViewMissing($key);
انتظار دارد کلاینت به URI داده شده هدایت (redirect) شود.
->assertRedirectedTo($uri, $with =[]);
انتظار دارد کلاینت به route ارائه شده به عنوان آرگومان هدایت (redirect) شود.
->assertRedirectedToRoute($name,$parameters = [], $with = []);
انتظار دارد کلاینت به action ارائه شده به عنوان آرگومان هدایت (redirect) شود.
->assertRedirectedToAction($name,$parameters = [], $with = []);
انتظار دارد session دارای مقدار پاس داده شده به عنوان آرگومان باشد.
->assertSessionHas($key, $value =null);
انتظار دارد session دارای لیست مقادیر ارائه شدهباشد.
->assertSessionHasAll(array$bindings);
انتظار دارد session دارای error های مرتبط (bindشده) باشد (که به عنوان آرگومان پاس داده شده).
->assertSessionHasErrors($bindings= [], $format = null);
انتظار دارد session دارای ورودی های قدیمی باشد.
->assertHasOldInput();
انتظار دارد session کلید ارائه شده به عنوان آرگومان را نداشته باشد.
->assertSessionMissing($key);

کار با بانک های اطلاعاتی

Laravel تعداد زیادی ابزار مفید در اختیار برنامه نویس قرار می دهد که تست گیری از اپلیکیشن های پایگاه داده محور را آسان می سازد. ابتدا می توان با فراخوانی تابع کمکی seeInDatabase انتظار داشت (assert کرد) که داده هایی که با معیارهای خاصی منطبق هستند در پایگاه داده وجود دارند یا خیر. برای مثال، جهت بررسی اینکه آیا یک رکورد در جدول users وجود دارد که دارای ستون (تحت عنوان) email با مقدارsally@example.com هست یا خیر، می توان به صورت زیر اقدام کرد:

public function testDatabase()
{
   // Make call to application...
   $this->seeInDatabase('users', ['email' => 'sally@example.com']);
}

لازم به ذکر است که متد seeInDatabase و دیگر توابع کمکی (helper) نظیر آن، همگی صرفا به منظور راحتی تست گیری از اپلیکیشن ارائه شده است. شما می توانید هر یک از توابع درون ساخته ی assertion از ابزار PHPUnit را برای تکمیل تست های خود بکار ببرید.

بازگردانی (reset) پایگاه داده پس از هر تست

گاهی لازم می شود پایگاه داده ی خود را پس از هر با تست reset کرده تا داده های تست قبلی با داده های تست های بعدی تداخل پیدا نکند.

استفاده از Migrations

یک راه این است که پایگاه داده را پس از هر بار تست گیری به عقب برگردانید (rollback) و پیش از تست بعدی migrate (بروز رسانی) نمایید.Laravel با فراهم کردن یک trait ساده به نام DatabaseMigrations به صورت خودکار این عملیات را برای شما مدیریت می کند. کافی استtrait مزبور را مانند زیر در کلاس تست خود بکار ببرید:

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
   use DatabaseMigrations;
   
   public function testBasicExample()
   {
       $this->visit('/')
            ->see('Laravel 5');
   }
}

استفاده از تراکنش (transaction)

روش دیگر این است که مورد تست (test case) خود را داخل یک تراکنش قرار دهید. باز هم Laravel با ارائه ی trait ای به نامDatabaseTransactions به کمک شما می آید و این فرایند را به صورت خودکار برای شما مدیرت می کند.

<?php
use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
   use DatabaseTransactions;
   
   public function testBasicExample()
   {
       $this->visit('/')
            ->see('Laravel 5');
   }
}
نکته:

trait نام برده در بالا، تنها connection پیش فرض را در تراکنش قرار می دهد.

Model factory ها

به هنگام تست اپلیکیشن، معمولا لازم می شود تعدادی رکورد را پیش از راه اندازی تست، داخل پایگاه داده وارد نمایید. بجای اینکه هر ستون را در زمان تعریف داده های آزمایشی به صورت دستی مقداردهی کنید، می توانید با استفاده از factory های لاراول مجموعه ای از attribute های پیش فرض را برای مدل های Eloquent خود ایجاد کرده و مقادیر ستون ها را از این طریق مشخص نمایید. برای شروع نگاهی به فایلdatabase/factories/ModelFactory.php در اپلیکیشن خود بیاندازید. خواهید دید که این فایل، factory زیر را به صورت آماده و از قبل تنظیم شده در اختیار دارد:

$factory->define(App\User::class, function (Faker\Generator $faker) {
   return [
       'name' => $faker->name,
       'email' => $faker->email,
       'password' => bcrypt(str_random(10)),
       'remember_token' => str_random(10),
   ];
});

داخل بدنه ی تابع Closure، که در واقع تعریف factory می باشد، می توانید مقادیر آزمایشی (test values) پیش فرض تمامی attribute ها را در مدل برگردانید. Closure نمونه ای از کتابخانه ی Faker زبان PHP را به عنوان ورودی دریافت می کند که به شما اجازه می دهد به راحتی انواع مختلفی از داده های تصادفی را برای تست ایجاد کنید.
می توانید factory های مورد نیاز خود را به فایل ModelFactory.php اضافه کنید. همچنین می توانید به ازای هر مدل فایل های factoryمتعددی ایجاد نمایید. این کار به سازماندهی بهتر کدهای شما کمک می کند. به عنوان مثال می توانید فایل های UserFactory.php وCommentFactory.php را داخل پوشه ی database/factories خود ایجاد نمایید.

چندین factory برای یک مدل Eloquent

گاهی لازم می شود برای یک (کلاس) مدل Eloquent، چندین factory داشته باشید. برای مثال، ممکن است بخواهید علاوه بر کاربران عادی، برای کاربران "Administrator" نیز یک factory داشته باشید. برای ایجاد این factory ها کافی است متد defineAs را فراخوانی نمایید:

$factory->defineAs(App\User::class, 'admin', function ($faker) {
   return [
       'name' => $faker->name,
       'email' => $faker->email,
       'password' => str_random(10),
       'remember_token' => str_random(10),
       'admin' => true,
   ];
});

بجای اینکه تمامی attribute ها را از factory پایه ی خود (base user factory) duplicate (تکثیر) کنید، می توانید متد raw را برای بازیابیattribute های پایه بکار ببرید. پس از بازیابی attribute ها، کافی است مقادیر مورد نیاز خود را به آن ها اضافه نمایید:

$factory->defineAs(App\User::class, 'admin', function ($faker) use ($factory) {
   $user = $factory->raw(App\User::class);
   return array_merge($user, ['admin' => true]);
});

استفاده از Factory ها در تست

بعد از تعریف factory ها، می توانید آن ها را به راحتی در تست ها و در فایل های اولیه ی پایگاه داده (seed file) خود بکار ببرید. در زیر به مثال های ساده ای از ایجاد مدل می پردازیم. ابتدا با استفاده از متد make مدل های مورد نظرمان را ایجاد می کنیم. دقت داشته باشید که این متد صرفا مدل جدید ایجاد می کند و وظیفه ی آن ذخیره ی مدل های ایجاد شده را در پایگاه داده نیست:

public function testDatabase()
{
   $user = factory(App\User::class)->make();
   // Use model in tests...
}

برای بازنویسی (override) برخی از مقادیر پیش فرض مدل، کافی است آن مقادیر را به صورت آرایه به متد make پاس دهید. تنها مقادیر ارسالی به عنوان آرگومان در مدل جایگزین می شوند و باقی مقادیر دست نخورده باقی می مانند (بر روی مقادیر از پیش تنظیم شده توسط factory باقی می مانند):

$user = factory(App\User::class)->make([
   'name' => 'Abigail',
  ]);

می توانید Collection ای از مدل های متعدد ایجاد کرده یا مدل های متعددی از یک نوع خاص تعریف کنید:

// Create three App\User instances...
$users = factory(App\User::class, 3)->make();
// Create an App\User "admin" instance...
$user = factory(App\User::class, 'admin')->make();
// Create three App\User "admin" instances...
$users = factory(App\User::class, 'admin', 3)->make();

ذخیره ی دائمی مدل های تولید شده توسط factory

متد create علاوه بر ایجاد نمونه مدل های متعدد، مدل های تولید شده را با فراخوانی متد save از توابع Eloquent، به صورت دائمی در پایگاه داده ذخیره می کند:

public function testDatabase()
{
   $user = factory(App\User::class)->create();
   // Use model in tests...
}

می توانید مقدار attribute ها را در مدل بازنویسی نمایید. برای این منظور بایستی آرایه ای از مقادیر را (برای جایگزین شدن) به عنوان آرگومان به تابع create ارسال نمایید:

$user = factory(App\User::class)->create([
   'name' => 'Abigail',
  ]);

افزودن relation (رابطه) به مدل ها

می توانید چندین مدل را به طور دائمی در پایگاه داده ذخیره کنید. در این مثال، یک relation نیز به مدل های ایجاد شده اضافه می کنیم. پس از استفاده از متد create برای ایجاد چندین مدل، یک نمونه collection به عنوان خروجی در اختیار شما قرار می گیرد و این امکان را برای شما فراهم می آورد تا از تمامی توابع کارآمد collection استفاده نمایید. از جمله ی این توابع می توان به متد each در نمونه ی زیر اشاره کرد:

$users = factory(App\User::class, 3)
          ->create()
          ->each(function($u) {
               $u->posts()->save(factory(App\Post::class)->make());
           });

Relations &Attribute Closures (اضافه کردن رابطه هایی به مدل از طریق attribute های تابع Closure)

می توانید از طریق attribute های پاس داده شده به تابع Closure در تعریف factory، رابطه هایی (relationship) را به مدل های خود اضافه کنید. به عنوان مثال، اگر می خواهید حین ایجاد یک نمونه Post جدید یک نمونه User هم ایجاد کنید، می توانید به صورت زیر اقدام نمایید:

$factory->define(App\Post::class, function ($faker) {
   return [
       'title' => $faker->title,
       'content' => $faker->paragraph,
       'user_id' => function () {
           return factory(App\User::class)->create()->id;
       }
   ];
});

این توابع Closure همچنین attribute های (ارزیابی شده ی) آرایه موجود در تعریف factory را به عنوان ورودی دریافت می کنند:

$factory->define(App\Post::class, function ($faker) {
   return [
       'title' => $faker->title,
       'content' => $faker->paragraph,
       'user_id' => function () {
           return factory(App\User::class)->create()->id;
       },
       'user_type' => function (array $post) {
           return App\User::find($post['user_id'])->type;
       }
   ];
});

Mocking (تقلید در تست کد)

Mock کردن event ها

در صورت نیاز به استفاده ی زیاد از سیستم رخداد (event system) در لاراول، بهتر است به هنگام تست گیری از اپلیکیشن، برخی از event ها را خاموش کرده یا نمونه ی ساختگی از آن ها بسازید (آن ها را mock کنید). به عنوان مثال تصور کنید می خواهید فرایند ثبت نام کاربر در سایت را تست کنید. در این سناریو مسلما لزومی ندارد تمامی handler های مربوط به رخداد UserRegistered اعلان (fire) شوند چرا که با اجرای اینhandler ها ایمیل های خوش آمدگویی به کاربر ارسال می شود و مانند آن و این آن چیزی نیست که می خواهید در تست اتفاق بیافتد.
Laravel با ارائه ی یک متد به نام expectsEvents به شما امکان می دهد از اعلان (fire) شدن یا نشدن event های مورد انتظار اطمینان حاصل کنید. البته این متد از اجرا شدن handler های مربوط به هر event جلوگیری می کند:

<?php
class ExampleTest extends TestCase
{
   public function testUserRegistration()
   {
       $this->expectsEvents(App\Events\UserRegistered::class);
       // Test user registration...
   }
}

می توانید با فراخوانی متد doesntExpectEvents مطمئن شوید که event های مدنظر اعلان (fire) نشده اند:

<?php
class ExampleTest extends TestCase
{
   public function testPodcastPurchase()
   {
       $this->expectsEvents(App\Events\PodcastWasPurchased::class);
       $this->doesntExpectEvents(App\Events\PaymentWasDeclined::class);
       // Test purchasing podcast...
   }
}

حال اگر می خواهید از اجرای تمامی event handler ها جلوگیری کنید، کافی است متد withoutEvents را در کد خود بکار ببرید:

<?php
class ExampleTest extends TestCase
{
   public function testUserRegistration()
   {
       $this->withoutEvents();
       // Test user registration code...
   }
}

Mock کردن job ها

گاهی لازم می شود تست کرده و مطمئن شوید که job های مورد نظر به هنگام دادن درخواست به اپلیکیشن، توسط controller ارسال (dispatch) می شوند. این امر به شما امکان می دهد route ها / controller های خود را به صورت مجزا و در واقع فارغ از منطق job تست نمایید. پس از آن در صورت لزوم می توانید خود job را در یک کلاس آزمایشی (test class) مستقل تست نمایید.
باز هم Laravel با ارائه ی متد expectsJobs این امکان را فراهم می کند تا بررسی کنید آیا job های مورد انتظار کاملا ارسال (dispatch) شده اند یا خیر. لازم به ذکر است که با فراخوانی این متد خود job اجرا نخواهد شد:

<?php
class ExampleTest extends TestCase
{
   public function testPurchasePodcast()
   {
       $this->expectsJobs(App\Jobs\PurchasePodcast::class);
       // Test purchase podcast code...
   }
}
نکته:

این متد تنها قادر به شناسایی job هایی هست که به واسطه ی متدهای dispatch ارائه شده توسط مشخصه ی (trait) DispatchesJobs و یا تابع کمکی dispatch ارسال می شوند. به عبارت دیگر job هایی که به صورت مستقیم به Queue::push ارسال می شوند را به هیچ وجه نمی شناسد.

Mock کردن Facade ها

به هنگام اجرای تست، گاهی لازم می شود فراخوانی یکی از facade های لاراول را تقلید (mock) کنید. به عنوان مثال متد action در کلاسcontroller زیر را در نظر بگیرید:

<?php
namespace App\Http\Controllers;
use Cache;
class UserController extends Controller
{
   
   public function index()
   {
       $value = Cache::get('key');
       //
   }
}

می توان فراخوانی Cache facade را با استفاده از متد shouldReceive تقلید (mock) کرد. این متد در خروجی یک نمونه از Mockery mock را برمی گرداند (Mockery یک فریم ورک ایجاد شی ساختگی / mock در PHP هست که می توان در تست واحد از آن استفاده کرد). از آنجایی که facade ها توسط service container ایجاد (resolve) و مدیریت می شوند، قابلیت تست آن ها نسبت به یک کلاس ساده static بسیار بیشتر می باشد. در زیر با بهره گیری از متد نام برده در بالا، فراخوانی Cache façade را تقلید (mock) می کنیم:

<?php
class FooTest extends TestCase
{
   public function testGetIndex()
   {
       Cache::shouldReceive('get')
                   ->once()
                   ->with('key')
                   ->andReturn('value');
       $this->visit('/users')->see('value');
   }
}
نکته:

به هیچ وجه توصیه نمی شود از Request façade نمونه ی ساختگی بسازید (mock کنید). بجای این کار می توانید ورودی دلخواه را به هنگام اجرای تست، به متدهای کمکی HTTP نظیر call و Post ارسال نمایید.

1395/04/06 6455 1400
رمز عبور : tahlildadeh.com یا www.tahlildadeh.com
نظرات شما

نظرات خود را ثبت کنید...