توابع assertEquals()
و assertNull()
assert (از این به بعد تابع می نامیم) assertEquals()
دو مقدار را با هم مقایسه می کند تا ببیند مساوی هستند یا خیر. ما می خواهیم از این تابع برای چک کردن مقدار برگشتی از تابع takeOne()
استفاده کنیم. با توجه به اینکه تابع takeOne()
مقدار Null بر می گرداند اگر متغیر box خالی باشد، می توانیم از تابع assertNull()
هم برای چک کردنش استفاده کنیم.
بر خلاف assertTrue()
, assertFalse()
, و assertNull()
, تابع assertEquals()
دو پارامتر میگیرد. اولی مقداری که مورد انتظار است و دومی مقدار واقعی.
ما می توانیم این توابع را در کلاس مان به شکل زیر پیاده سازی کنیم:
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } public function testTakeOneFromTheBox() { $box = new Box(['torch']); $this->assertEquals('torch', $box->takeOne()); // Null, now the box is empty $this->assertNull($box->takeOne()); } }
حالا دستور phpunit را اجرا کنید و خروجی زیر را خواهید دید:
OK (3 tests, 6 assertions)
assertContains()
, assertCount()
, و assertEmpty()
در نهایت سه تابع داریم که می توانند با آرایه ها کار کنند و از آنها برای چک کردن مقدار برگشتی متد startsWith($item)
در کلاس Box
می توان استفاده کرد. تابع assertContains()
چک می کند که مقدار داده شده در آرایه هست یا خیر. تابع assertCount()
تعداد آیتم های داخل آرایه را با مقدار داده شده بررسی می کند و assertEmpty()
خالی بودن آرایه را تعیین می کند.
با توابع بالا می توانیم تست زیر را ایجاد کنیم:
<?php use App\Box; class BasicTest extends TestCase { public function testHasItemInBox() { $box = new Box(['cat', 'toy', 'torch']); $this->assertTrue($box->has('toy')); $this->assertFalse($box->has('ball')); } public function testTakeOneFromTheBox() { $box = new Box(['torch']); $this->assertEquals('torch', $box->takeOne()); // Null, now the box is empty $this->assertNull($box->takeOne()); } public function testStartsWithALetter() { $box = new Box(['toy', 'torch', 'ball', 'cat', 'tissue']); $results = $box->startsWith('t'); $this->assertCount(3, $results); $this->assertContains('toy', $results); $this->assertContains('torch', $results); $this->assertContains('tissue', $results); // Empty array if passed even $this->assertEmpty($box->startsWith('s')); } }
یکبار دیگر تست را ذخیره کرده و اجرا کنید تا نتیجه زیر را ببینید:
OK (4 tests, 9 assertions)
تبریک! شما کلاس Box
را با استفاده از ۷ تابع(Assertion) ساده PHPUnit تست کردید.با استفاده با همین توابع ساده می توانید کارهای زیادی انجام دهید و توابع پیچیده تر را یاد بگیرید تا تست های پیچیده تری بنویسید.
برنامه تان را تست کنید
Unit Testing هر جز برنامه شما در شرایط بسیار مختلفی می تواند کار می کند و قطعا باید بخشی از فرایند توسعه شما باشد، با این حال این تمام تستی نیست که شما باید انجام دهید. هنگامی که شما در حال ایجاد یک نرم افزار پیچیده، شامل view ها ناوبری و فرم ها، و … هستید حتما می خواهید این اجزاء را نیز آزمایش کنید. این جایی است که Helper های لاراول کارها را به آسانی انجام می دهند تقریبا در حد همان تست هایی که قبلا نوشتیم.
ما قبلا به فایل های پیش فرض داخل پوشه ./tests/
نگاهی انداختیم و از فایل ./tests/ExampleTest.php
صرف نظر کردیم.حالا آن را باز کنید باید شبیه کد زیر باشد:
<?php class ExampleTest extends TestCase { /** * A basic functional test example. * * @return void */ public function testBasicExample() { $this->visit('/') ->see('Laravel'); } }
همانطور که می بینید تست داخل آن بسیار ساده است.بدون هیچ دانش قبلی در مورد توابع کمک کننده (helper) می توانیم حدس بزنیم چه کاری انجام می شود:
- وقتی صفحه اصلی / را باز کردی
- باید حتما “Laravel” را ببینی!
اگر برنامه لاراول مان را اجرا کنید در صفحه اصلی آن می توانید عبارت “Laravel” را ببینید.پس این تست از نظر PHPUnit با موفقیت اجرا خواهد شد.
visit()
, see()
, و dontSee()
حالا می خواهیم تست خودمان را بنویسیم.
اول، فایل ./routes/web.php
راویرایش کنید، تا یک روت جدید به آن اضافه کنیم.
<?php Route::get('/', function () { return view('welcome'); }); Route::get('/alpha', function () { return view('alpha'); });
بعد، view مورد نیاز را در ./resources/views/alpha.blade.php
بسازید و کدهای زیر را که شامل کلمه Alpha
می شود، داخل آن کپی کنید:
<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> </body> </html>
حالا صفحه جدید را در مرورگرتان باز کنید تا از درست بودن کار تا اینجا مطمئن شوید: http://localhost:8000/alpha
و باید پیام “This is the Alpha page.” را ببینید.
حالا که تمپلیت مورد نظر را داریم یک تست جدید ایجاد می کنیم. دستور زیر را اجرا کنید:
php artisan make:test AlphaTest
حالا می خواهیم در تست بررسی کنیم که کلمه “alpha” در صفحه باشد ولی کلمه “beta” نباشد. برای این کار از تابع dontSee()
استفاده می کنیم که برعکس تابع see() کار می کند.
پس تست ساده ما شبیه این خواهد بود:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta'); } }
آن را ذخیره کنید و مثل قبل اجرا کنید. باید نتیجه درست باشد و پیام زیر را ببینید:
OK (5 tests, 12 assertions)
تست ها را اول بنویسید
یک مورد جالب در مورد تست ها این است که شما می توانید از نظریه Test Driven Development یا TDD استفاده کنید و تست های تان را اول بنویسید. یعنی اول تست ها را می نویسید و آنها را اجرا می کنید و می بینید که Fail می شوند . سپس کدهایتان را می نویسید جوری که تست ها را پاس کنند.بنابراین اجازه دهید همین کار را برای صفحه بعدی انجام دهیم.
اول، با دستور زیر کلاس BetaTest
را ایجاد کنید:
php artisan make:test BetaTest
بعد، کلاس فوق را تغییر دهید تا مسیر
/beta
را برای بودن کلمه “Beta” چک کند:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha'); } }
حالا تست را اجرا کنید. نتیجه باید پیام خطایی ناخوشایند مثل این باشد:
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. ....F. Time: 144 ms, Memory: 14.25Mb There was 1 failure: ۱) BetaTest::testDisplaysBeta A request to [http://localhost/beta] failed. Received status code [404]. ... FAILURES! Tests: 6, Assertions: 13, Failures: 1.
همانطور که می بینید یک خطا برای نبود روت داریم.پس آن را ایجاد می کنیم.
اول، فایل ./routes/web.php
را باز کرده و یک روت جدید به مسیر /beta
به آن اضافه می کنیم:
<?php Route::get('/', function () { return view('welcome'); }); Route::get('/alpha', function () { return view('alpha'); }); Route::get('/beta', function () { return view('beta'); });
و بعد view مورد نیاز را در مسیر ./resources/views/beta.blade.php
می سازیم:
<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> </body> </html>
حالا یکبار دیگر PHPUnit را اجرا کنید و نتیجه باید دوباره سبز شود!
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. ...... Time: 142 ms, Memory: 14.00Mb OK (6 tests, 15 assertions)
حالا ما اولین صفحه مان را با استفاده از متد Test Driven Development ساختیم.
click()
و seePageIs()
لاراول همچنین helper هایی برای کلیک کردن در تست، روی لینک های موجود درصفحه دارد و همچنین راهی برای اینکه ببینیم صفحه مقصد لینک کدام صفحه است.
فرض کنید می خواهیم با استفاده از دو متد فوق، لینک هایی بین صفحات beta و alpha اضافه کنیم.
اول، باید تست هایمان را بروز کنیم. کلاس تست AlphaTest
را باز کنید تا متد جدید به آن اضافه کنیم برای کلیک روی لینکی که در صفحه alpha قرار دارد و به صفحه beta می رود.
این تست شبیه کد زیر خواهد بود:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta'); } public function testClickNextForBeta() { $this->visit('/alpha') ->click('Next') ->seePageIs('/beta'); } }
دقت کنید که ما محتوای هر دو صفحه را در متد جدید testClickNextForBeta()
چک نمی کنیم. متدهای قبلی این کار را انجام می دهند، بنابراین برای ما فقط مهم این است که با کلیک روی لینک “Next” به صفحه /beta
می رویم یا خیر.
حالا هم می توانید تست را اجرا کنید ولی خطا خواهد داد زیرا هنوز HTML را بروز نکرده ایم.
بعد، کلاس BetaTest
را نیز مشابه کلاس alpha تغییر می دهیم:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha'); } public function testClickNextForAlpha() { $this->visit('/beta') ->click('Previous') ->seePageIs('/alpha'); } }
و نوبت بروز کردن HTML برای اضافه کردن لینک است.فایل ./resources/views/alpha.blade.php
را باز کنید و طبق کد زیر آن را تغییر دهید:
<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> <p><a href="/beta">Next</a></p> </body> </html>
همینطور برای ./resources/views/beta.blade.php
نیز:
<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> <p><a href="/alpha">Previous</a></p> </body> </html>
فایل ها را ذخیره کنید و تست را اجرا نمایید:
> ./vendor/bin/phpunit PHPUnit 4.8.19 by Sebastian Bergmann and contributors. F....F.. Time: 175 ms, Memory: 14.00Mb There were 2 failures: ۱) AlphaTest::testDisplaysAlpha Failed asserting that '<!DOCTYPE html> <html> <head> <title>Alpha</title> </head> <body> <p>This is the Alpha page.</p> <p><a href="/beta">Next</a></p> </body> </html> ' does not match PCRE pattern "/Beta/i". ۲) BetaTest::testDisplaysBeta Failed asserting that '<!DOCTYPE html> <html> <head> <title>Beta</title> </head> <body> <p>This is the Beta page.</p> <p><a href="/alpha">Previous</a></p> </body> </html> ' does not match PCRE pattern "/Alpha/i". FAILURES! Tests: 8, Assertions: 23, Failures: 2.
خوب تست ما یه جورایی خطا داده است. اگر با دقت به کد HTML مان نگاه کنید می بینید که حالا کلمات beta و alpha را به ترتیب در صفحات /alpha
و /beta
داریم. این به این معنی است که باید کمی تست ها را تغییر دهیم تا در این موقعیت کاذب قرار نگیرند.
داخل هر کدام از کلاس های AlphaTest
and BetaTest
متدهایی که با testDisplays*
شروع می شوند تغییر دهید تا این تداخل کلمات رفع شود.
بعد از این تغییر هر دو تست به این شکل خواهند بود:
تست ./tests/AlphaTest.php
می شود:
<?php class AlphaTest extends TestCase { public function testDisplaysAlpha() { $this->visit('/alpha') ->see('Alpha') ->dontSee('Beta page'); } public function testClickNextForBeta() { $this->visit('/alpha') ->click('Next') ->seePageIs('/beta'); } }
تست ./tests/BetaTest.php
می شود:
<?php class BetaTest extends TestCase { public function testDisplaysBeta() { $this->visit('/beta') ->see('Beta') ->dontSee('Alpha page'); } public function testClickNextForAlpha() { $this->visit('/beta') ->click('Previous') ->seePageIs('/alpha'); } }
حالا اگر تست ها را اجرا کنید همه چیز باید اوکی باشد. حالا ما صفحات جدیدمان و لینک های داخل شان را تست کردیم.