در این آموزش قصد داریم تا یک تست فرضی نوشته و نحوهٔ Pass/Fail شدن آن را مورد بررسی قرار دهیم که در همین راستا داخل پوشهٔ tests
فایلی با نامی دلخواه همچون TmpTest.php
نوشته و کدهای زیر را داخل آن مینویسیم:
<?php
class TmpTest extends \PHPUnit\Framework\TestCase
{
}
در تفسیر کدهای فوق میتوان گفت که کلاسی ساختهایم تحت عنوان TmpTest
که کلیهٔ خصوصیات خود را از کلاس TestCase
فریمورک PHPUnit به ارث میبرد. نیاز به توضیح نیست حالت دیگری که میتوانیم به این کلاس دسترسی داشته باشیم به صورت زیر است:
<?php
use PHPUnit\Framework\TestCase;
class TmpTest extends TestCase
{
}
همانطور که میبینیم، در خط دوم با استفاده از کیورد use
مسیر این کلاس را وارد فایل خود کرده و پس از کیورد extends
صرفاً نام کلاس مذکور را نوشتهایم. به طور کلی، از این پس فایل autoload.php
داخل پوشهٔ vendor
به صورت خودکار این کلاس و سایر کلاسهای مورد استفاده را به برنامه ایمپورت خواهد کرد.
در این مرحله از کار برای آنکه ببینیم رفتار PHPUnit چگونه است، مجدد تست مد نظر را ران میکنیم به طوری که در خروجی خواهیم داشت:
/var/www/phpunit$ ./vendor/bin/phpunit
PHPUnit 8.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.4-1+ubuntu18.04.1+deb.sury.org+3
Configuration: /var/www/phpunit/phpunit.xml
W 1 / 1 (100%)
Time: 21 ms, Memory: 4.00 MB
There was 1 warning:
1) Warning
No tests found in class "TmpTest".
WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.
همانطور که ملاحظه میشود، هشداری در معرض دیدمان قرار میگیرد با این مضمون که «هیچگونه تستی داخل کلاس مذکور یافت نشد.» که برای رفع این هشدار، کدهای فوق را به صورت زیر تکمیل میکنیم:
<?php
use PHPUnit\Framework\TestCase;
class TmpTest extends TestCase
{
public function testTrueAssertsToTrue()
{
$this->assertTrue(true);
}
}
ابتدا با اجرای مجدد تست، خروجی را ملاحظه کرده سپس به تفسیر کدهای فوق خواهیم پرداخت:
/var/www/phpunit$ ./vendor/bin/phpunit
PHPUnit 8.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.4-1+ubuntu18.04.1+deb.sury.org+3
Configuration: /var/www/phpunit/phpunit.xml
. 1 / 1 (100%)
Time: 20 ms, Memory: 4.00 MB
OK (1 test, 1 assertion)
میبینیم که عبارت (OK (1 test, 1 assertion در خروجی چاپ شده است به این معنی که یک تست با موفقیت انجام شده است اما پیش از ادامهٔ آموزش نیاز است تا با مفهوم Assertion در یونیت تست بیشتر آشنا شویم که در ادامه این موضوع را مورد بررسی قرار خواهیم داد.
آشنایی با مفهوم Assertion
کلمهٔ Assertion از فعل Assert به معنی «اظهار کردن» یا «ادعا کردن» صحت چیزی گرفته شده است و این اصطلاح در یونیت تست بدان معنا است که وقتی یک تست انجام میشود، طی آن ما به عنوان دولوپر یکسری ادعاها میکنیم بدین صورت که مثلاً یک تابع خاص باید خروجی true
را ریترن کند و یا کد وضعیت اچتیتیپی تابعی دیگر میباید معادل با ۲۰۰ باشد که چیزهایی از این دست اصطلاحاً Assertion گفته میشوند. به عبارتی، ما در تست خود ادعاهایی داریم که پس از ران کردن تست کلیهٔ آن ادعاها میباید ثابت شوند.
حال با مد نظر قرار دادن این نکته، ابتدا به تفسیر فانکشن ()testTrueAssertsToTrue
پرداخته سپس خواهیم دید که در این تست چه ادعاهایی کردهایم. آنچه در ارتباط با توابعی که در یک کلاس تست مینویسیم حائز اهمیت است اینکه نام کلیهٔ توابع میباید با کلمهٔ test
آغاز شود که فقط و فقط در این صورت است که فریمورک PHPUnit قادر به تشخیص تستها میباشد.
با در نظر گرفتن این اصل، نامی دلخواه برای تنها تابع موجود تحت عنوان ()testTrueAssertsToTrue
در نظر گرفتهایم که این تابع هیچکار خاصی انجام نمیدهد جز اینکه در آن ادعا کردهایم مقدار true
برابر با true
است!
()assertTrue
یکی از متدهای تعبیهشده داخل کلاس TestCase
است که این وظیفه را دارا است تا اگر هر چیزی که مقدارش true
است را به عنوان آرگومان ورودی به آن پاس دهیم، مهرتأیید روی ادعا (Assertion) ما زده و خروجی OK را در اختیارمان قرار دهد که برای درک بهتر این موضوع، کدهای فوق را به صورت زیر تغییر داده و مجدد تست را اجرا میکنیم:
public function testTrueAssertsToTrue()
{
$result = (1 === 1);
$this->assertTrue($result);
}
در واقع، متغیری تعریف کردهایم تحت عنوان result$
و مقدار آن را برابر با (1 === 1) قرار دادهایم بدان معنا که مقدار عدد ۱ را برابر با مقدار عدد ۱ قرار دادهایم که مسلماً چون این دو عدد با هم برابر هستند، نتیجهٔ آن همواره true
خواهد بود؛ سپس این متغیر را به عنوان آرگومان تابع ()assertTrue
در نظر گرفتهایم که با اجرای این تست خواهیم دید:
/var/www/phpunit$ ./vendor/bin/phpunit
PHPUnit 8.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.4-1+ubuntu18.04.1+deb.sury.org+3
Configuration: /var/www/phpunit/phpunit.xml
. 1 / 1 (100%)
Time: 21 ms, Memory: 4.00 MB
OK (1 test, 1 assertion)
میبینیم که خروجی همچون گذشته OK است. آنچه در ارتباط با خروجی فوق باید مد نظر قرار دهیم، بخش زیر است:
. 1 / 1 (100%)
همانطور که میبینیم، یک نقطه قرار داده شده است و این نقطه حاکی از آن است که ما کلاً یک تست داریم و این در حالی است که اگر برای پروژهٔ خود ده تست نوشته باشیم، شاهد ده نقطهٔ پشت سر هم خواهیم بود.
اکنون در ادامه و به منظور درک ماهیت تابع ()assertTrue
، کدهای فوق را به صورت زیر تغییر میدهیم:
public function testTrueAssertsToTrue()
{
$this->assertTrue(false);
}
که پس از اجرای مجدد تست در خروجی خواهیم دید:
/var/www/phpunit$ ./vendor/bin/phpunit
PHPUnit 8.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.4-1+ubuntu18.04.1+deb.sury.org+3
Configuration: /var/www/phpunit/phpunit.xml
F 1 / 1 (100%)
Time: 21 ms, Memory: 4.00 MB
There was 1 failure:
1) TmpTest::testTrueAssertsToTrue
Failed asserting that false is true.
/var/www/phpunit/tests/TmpTest.php:8
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
همانطور که ملاحظه میشود، .FAILURES! Tests: 1, Assertions: 1, Failures: 1 با رنگ قرمز در معرض دیدمان قرار گرفته است که در تفسیر اجرای فوق میتوان گفت که ما با استفاده از متد ()assertTrue
در صدد آن هستیم که ادعا کنیم مقدار false
برابر با true
است که مسلماً چنین چیزی امکانپذیر نمیباشد و از همین روی با گزارهای همچون «.Failed asserting that false is true» مواجه میشویم حاکی از آنکه «در نظر گرفتن مقدار false
برابر با true
موفقیتآمیز نبوده است.» اما چنانچه بخواهیم این تست پاس گردد، میتوانیم به صورت زیر از متد دیگری تحت عنوان ()assertFalse
استفاده نماییم:
public function testTrueAssertsToTrue()
{
$this->assertFalse(false);
}
اکنون خروجی تست به صورت زیر خواهد بود:
/var/www/phpunit$ ./vendor/bin/phpunit
PHPUnit 8.1.4 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.4-1+ubuntu18.04.1+deb.sury.org+3
Configuration: /var/www/phpunit/phpunit.xml
. 1 / 1 (100%)
Time: 20 ms, Memory: 4.00 MB
در واقع، ما با استفاده از تابع ()assertFalse
قصد داریم ادعا کنیم که پارامتر ورودی برابر با false
است و از آنجا که مسلماً false
برابر با false
است، خروجی این تست OK خواهد بود.