گام 17: آزمودن (Testing)

5.0 version
Maintained

آزمودن (Testing)

از آنجا که ما شروع به افزودن قابلیت‌های بیشتر و بیشتر در برنامه می‌کنیم‌‌، احتمالاً زمان مناسبی برای صحبت در‌باره‌ی آزمودن اپلیکیشن می‌باشد.

حقیقت خنده‌دار: من هنگام نوشتن آزمون‌ها در این فصل، یک باگ پیدا کردم.

سیمفونی برای آزمون‌های واحد (unit test)، به PHPUnit تکیه دارد. بیایید آن را نصب کنیم:

1
$ symfony composer req phpunit

نوشتن آزمون‌های واحد (Unit Tests)

SpamChecker اولین کلاسی است که می‌خواهیم برای آن آزمون بنویسیم. یک آزمون واحد تولید کنید:

1
$ symfony console make:unit-test SpamCheckerTest

آزمودن SpamChecker یک چالش است زیرا مطمئناً نمی‌خواهیم واقعاً API مربوط به Akismet را فراخوانی کنیم. می‌خواهیم API را تقلید (mock) کنیم.

بیایید اولین آزمون را برای زمانی که API یک خطا بازمی‌گرداند، بنویسیم:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--- a/tests/SpamCheckerTest.php
+++ b/tests/SpamCheckerTest.php
@@ -2,12 +2,26 @@

 namespace App\Tests;

+use App\Entity\Comment;
+use App\SpamChecker;
 use PHPUnit\Framework\TestCase;
+use Symfony\Component\HttpClient\MockHttpClient;
+use Symfony\Component\HttpClient\Response\MockResponse;
+use Symfony\Contracts\HttpClient\ResponseInterface;

 class SpamCheckerTest extends TestCase
 {
-    public function testSomething()
+    public function testSpamScoreWithInvalidRequest()
     {
-        $this->assertTrue(true);
+        $comment = new Comment();
+        $comment->setCreatedAtValue();
+        $context = [];
+
+        $client = new MockHttpClient([new MockResponse('invalid', ['response_headers' => ['x-akismet-debug-help: Invalid key']])]);
+        $checker = new SpamChecker($client, 'abcde');
+
+        $this->expectException(\RuntimeException::class);
+        $this->expectExceptionMessage('Unable to check for spam: invalid (Invalid key).');
+        $checker->getSpamScore($comment, $context);
     }
 }

کلاس MockHttpClient این امکان را می‌دهد تا هر سرور HTTP را تقلید کنیم. این کلاس آرایه‌ی از نمونه‌های MockResponse را که حاوی بدنه (body) و سربرگ‌های (headers) پاسخ مورد نظر هستند، می‌گیرد.

سپس متد getSpamScore()``را فراخوانی می‌کنیم و بررسی می‌کنیم که آیا یک استثناء توسط متد ``getSpamScore() در PHPUnit پرتاب می‌شود یا خیر.

برای بررسی قبولی در آزمون، آزمون را اجرا کنید:

1
$ symfony php bin/phpunit

بیایید برای مسیر خوشحال، آزمون اضافه کنیم:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
--- a/tests/SpamCheckerTest.php
+++ b/tests/SpamCheckerTest.php
@@ -24,4 +24,32 @@ class SpamCheckerTest extends TestCase
         $this->expectExceptionMessage('Unable to check for spam: invalid (Invalid key).');
         $checker->getSpamScore($comment, $context);
     }
+
+    /**
+     * @dataProvider getComments
+     */
+    public function testSpamScore(int $expectedScore, ResponseInterface $response, Comment $comment, array $context)
+    {
+        $client = new MockHttpClient([$response]);
+        $checker = new SpamChecker($client, 'abcde');
+
+        $score = $checker->getSpamScore($comment, $context);
+        $this->assertSame($expectedScore, $score);
+    }
+
+    public function getComments(): iterable
+    {
+        $comment = new Comment();
+        $comment->setCreatedAtValue();
+        $context = [];
+
+        $response = new MockResponse('', ['response_headers' => ['x-akismet-pro-tip: discard']]);
+        yield 'blatant_spam' => [2, $response, $comment, $context];
+
+        $response = new MockResponse('true');
+        yield 'spam' => [1, $response, $comment, $context];
+
+        $response = new MockResponse('false');
+        yield 'ham' => [0, $response, $comment, $context];
+    }
 }

فراهم‌کنندگان داده در PHPUnit، به ما اجازه می‌دهد تا یک منطق آزمون یکسان را برای مورد آزمون‌های (test case) مختلف مجدداً استفاده کنیم.

نوشتن آزمون‌های کارکردی (Functional Tests) برای کنترلرها

آزمودن کنترلرها نسبت به آزمودن کلاس‌های PHP «معمولی»، کمی متفاوت است. زیرا که می‌خواهیم آن‌ها را در زمینه‌ی درخواست‌های HTTP اجرا کنیم.

تعدادی وابستگی اضافی که برای آزمون‌های کارکردی لازم هستند را نصب کنید:

1
$ symfony composer require browser-kit --dev

یک آزمون کارکردی برای کنترلر Conference ایجاد کنید:

tests/Controller/ConferenceControllerTest.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
namespace App\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ConferenceControllerTest extends WebTestCase
{
    public function testIndex()
    {
        $client = static::createClient();
        $client->request('GET', '/');

        $this->assertResponseIsSuccessful();
        $this->assertSelectorTextContains('h2', 'Give your feedback');
    }
}

اولین آزمون بررسی می‌کند که صفحه‌ی اصلی، پاسخ HTTP از نوع ۲۰۰ باز می‌گرداند یا خیر.

متغیر $client یک مرورگر را شبیه‌سازی می‌کند. البته به جای فراخوانی HTTP به سرور، اپلیکیشن سیمفونی را به صورت مستقیم فراخوانی می‌کند. این راهبرد مزایای متعددی دارد: در مقایسه با رفت و برگشت میان کلاینت و سرور سریع‌تر است، همچنین اجازه می‌دهد تا آزمون‌ها، وضعیت سرویس‌ها را پس از هر درخواست HTTP بازرسی کنند.

ادعاهایی (Assertions) همچون assertResponseIsSuccessful بر روی PHPUnit اضافه شده‌اند تا کار شما را راحت کنند. تعداد زیادی ادعا توسط سیمفونی تعریف شده است.

نکته

ما از / به عنوان URL استفاده کردیم به جای اینکه از طریق راه‌یاب (router) آن را تولید کنیم. هدف و علت این کار آن است که URLهای کاربر نهایی نیز بخشی از چیزی است که می‌خواهیم آزموده شود. اگر شما مسیرِ راه (route path) را تغییر دهید، آزمون شکست می‌خورد و به خوبی یادآور می‌شود که شما احتمالاً باید URL قدیمی را به URL جدید بازهدایت کنید تا برای موتورهای جستجو و وب‌سایت‌هایی که به وب‌سایت شما پیوند می‌دهند، مشکلی پیش نیاید.

توجه

می‌توانستیم آزمون را به کمک باندلِ maker تولید کنیم:

1
$ symfony console make:functional-test Controller\\ConferenceController

آزمون‌های PHPUnit در محیط اختصاصی test اجرا می‌شوند. ما باید رمز AKISMET_KEY را برای این محیط تنظیم کنیم:

1
$ APP_ENV=test symfony console secrets:set AKISMET_KEY

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

1
$ symfony php bin/phpunit tests/Controller/ConferenceControllerTest.php

نکته

زمانی که آزمون شکست می‌خورد، ممکن است بازرسی شیء پاسخ مفید واقع شود. از طریق $client->getResponse() و echo به آن دست پیدا کنید تا ببینید به چه شکل است.

تعریف Fixture‌ها

برای اینکه قادر به آزمودن لیست کامنت‌ها، صفحه‌بندی و فرم ارسال باشیم، ما نیاز داریم تا پایگاه‌داده را با مقداری داده پر کنیم. همچنین می‌خواهیم که این داده‌ها بین آزمونهای مختلف یکسان باشد تا موجب قبولی آزمون شود. Fixtureها دقیقاً چیزی هستند که نیازشان داریم.

باندل Doctrine Fixtures را نصب کنید:

1
$ symfony composer req orm-fixtures --dev

در هنگام نصب، پوشه‌ی جدید src/DataFixtures/ به همراه یک کلاس نمونه ایجاد شده که آماده‌ی شخصی‌سازی است. فعلاً ۲ کنفرانس و یک کامنت به آن اضافه کنید:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
--- a/src/DataFixtures/AppFixtures.php
+++ b/src/DataFixtures/AppFixtures.php
@@ -2,6 +2,8 @@

 namespace App\DataFixtures;

+use App\Entity\Comment;
+use App\Entity\Conference;
 use Doctrine\Bundle\FixturesBundle\Fixture;
 use Doctrine\Common\Persistence\ObjectManager;

@@ -9,8 +11,24 @@ class AppFixtures extends Fixture
 {
     public function load(ObjectManager $manager)
     {
-        // $product = new Product();
-        // $manager->persist($product);
+        $amsterdam = new Conference();
+        $amsterdam->setCity('Amsterdam');
+        $amsterdam->setYear('2019');
+        $amsterdam->setIsInternational(true);
+        $manager->persist($amsterdam);
+
+        $paris = new Conference();
+        $paris->setCity('Paris');
+        $paris->setYear('2020');
+        $paris->setIsInternational(false);
+        $manager->persist($paris);
+
+        $comment1 = new Comment();
+        $comment1->setConference($amsterdam);
+        $comment1->setAuthor('Fabien');
+        $comment1->setEmail('[email protected]');
+        $comment1->setText('This was a great conference.');
+        $manager->persist($comment1);

         $manager->flush();
     }

زمانی که fixtureها را بار بگیریم، تمام داده‌ها پاک خواهد شد؛ از جمله کاربر مدیر. بیایید برای جلوگیری از این امر، کاربر مدیر را به fixtureها اضافه کنیم:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
--- a/src/DataFixtures/AppFixtures.php
+++ b/src/DataFixtures/AppFixtures.php
@@ -2,13 +2,22 @@

 namespace App\DataFixtures;

+use App\Entity\Admin;
 use App\Entity\Comment;
 use App\Entity\Conference;
 use Doctrine\Bundle\FixturesBundle\Fixture;
 use Doctrine\Common\Persistence\ObjectManager;
+use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;

 class AppFixtures extends Fixture
 {
+    private $encoderFactory;
+
+    public function __construct(EncoderFactoryInterface $encoderFactory)
+    {
+        $this->encoderFactory = $encoderFactory;
+    }
+
     public function load(ObjectManager $manager)
     {
         $amsterdam = new Conference();
@@ -30,6 +39,12 @@ class AppFixtures extends Fixture
         $comment1->setText('This was a great conference.');
         $manager->persist($comment1);

+        $admin = new Admin();
+        $admin->setRoles(['ROLE_ADMIN']);
+        $admin->setUsername('admin');
+        $admin->setPassword($this->encoderFactory->getEncoder(Admin::class)->encodePassword('admin', null));
+        $manager->persist($admin);
+
         $manager->flush();
     }
 }

نکته

اگر به خاطر نمی‌آورید که کدام سرویس را برای انجام یک وظیفه نیاز دارید، از فرمان debug:autowiring به همراه یک کلیدواژه استفاده کنید:

1
$ symfony console debug:autowiring encoder

بارگرفتن Fixtureها

fixtureها را به پایگاه‌داده بار بگیرید. آگاه باشید که این کار تمام داده‌های فعلی ذخیره‌شده در پایگاه‌داده را پاک می‌کند (اگر می‌خواهید جلوی این رفتار را بگیرید، به مطالعه ادامه دهید).

1
$ symfony console doctrine:fixtures:load

Crawl کردن یک وب‌سایت در آزمون‌های کارکردی

همانطور که دیدیم، HTTP client در آزمون‌ها برای شبیه‌سازی مرورگر استفاده می‌شود. بنابراین می‌توانیم درون یک وب‌سایت پیمایش کنیم انگار که در حال استفاده از یک مرورگر بی‌سر (headless) هستیم.

یک آزمون جدید بیافزایید که از درون صفحه‌ی اصلی، بر روی صفحه‌ی یک کنفرانس کلیک می‌کند:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
--- a/tests/Controller/ConferenceControllerTest.php
+++ b/tests/Controller/ConferenceControllerTest.php
@@ -14,4 +14,19 @@ class ConferenceControllerTest extends WebTestCase
         $this->assertResponseIsSuccessful();
         $this->assertSelectorTextContains('h2', 'Give your feedback');
     }
+
+    public function testConferencePage()
+    {
+        $client = static::createClient();
+        $crawler = $client->request('GET', '/');
+
+        $this->assertCount(2, $crawler->filter('h4'));
+
+        $client->clickLink('View');
+
+        $this->assertPageTitleContains('Amsterdam');
+        $this->assertResponseIsSuccessful();
+        $this->assertSelectorTextContains('h2', 'Amsterdam 2019');
+        $this->assertSelectorExists('div:contains("There are 1 comments")');
+    }
 }

بیایید به فارسی تشریح کنیم که چه اتفاقی درون این آزمون می‌افتد:

  • همچون آزمون اول، به صفحه‌ی اصلی می‌رویم؛
  • متد request() یک نمونه Crawler بازمی‌گرداند که کمک می‌کند تا المان‌های درون صفحه را پیدا کنیم (مثل پیوندها، فرم‌ها، یا هرچیزی که بتوان از طریق انتخابگرهای CSS یا XPath به آن دست یافت)؛
  • به لطف انتخابگر CSS (CSS selector)، ما ادعا می‌کنیم که ۲ کنفرانس در صفحه‌ی اصلی لیست شده است؛
  • سپس بر روی پیوند «View» کلیک می‌کنیم (از آنجایی که سیمفونی نمی‌تواند در آن واحد بر روی بیش از یک پیوند کلیک نماید، بر روی اولین موردی که پیدا کند را کلیک می‌کند)؛
  • عنوان صفحه، پاسخ و جزء <h2> صفحه را ادعا می‌کنیم تا اطمینان یابیم در صفحه‌ی درست قرار داریم (همچنین می‌توانیم راه -route- را بررسی کنیم که مطابقت داشته باشد)؛
  • در نهایت، ادعا می‌کنیم که یک کامنت در صفحه وجود دارد. div:contains() یک انتخابگر معتبر CSS نیست، اما سیمفونی تعدادی افزودنی خوب دارد که آن‌ها را از jQuery قرض گرفته است.

به جای کلیک کردن بر روی متن (به عبارت دیگر همان View)، می‌توانستیم پیوند را از طریق انتخابگر CSS هم انتخاب کنیم:

1
$client->click($crawler->filter('h4 + p a')->link());

بررسی کنید که آزمون جدید سبز است:

1
$ symfony php bin/phpunit tests/Controller/ConferenceControllerTest.php

کارکردن با یک پایگاه‌داده‌ی آزمون

به صورت پیشفرض، آزمون‌ها را در محیط سیمفونی test همانگونه که در فایل phpunit.xml.dist تعریف شده است، اجرا می‌کند:

phpunit.xml.dist
1
2
3
4
5
<phpunit>
    <php>
        <server name="APP_ENV" value="test" force="true" />
    </php>
</phpunit>

اگر می‌خواهید از یک پایگاه‌داده‌ی دیگری برای آزمون‌هایتان استفاده کنید، متغیر محیط DATABASE_URL را در فایل .env.test بازنویسی کنید:

1
2
3
4
5
6
7
8
--- a/.env.test
+++ b/.env.test
@@ -1,4 +1,5 @@
 # define your env variables for the test env here
+DATABASE_URL=postgres://main:[email protected]:32773/test?sslmode=disable&charset=utf8
 KERNEL_CLASS='App\Kernel'
 APP_SECRET='$ecretf0rt3st'
 SYMFONY_DEPRECATIONS_HELPER=999999

بارگرفتن fixtureها برای محیط/پایگاه‌داده‌ی test:

1
$ APP_ENV=test symfony console doctrine:fixtures:load

برای باقی این گام، ما متغیر محیط DATABASE_URL را بازتعریف نمی‌کنیم. استفاده از همان پایگاه‌داده‌ی محیط dev برای آزمون‌ها، مزیت‌هایی دارد که در گام بعدی خواهیم دید.

ارسال یک فرم در آزمون کارکردی

می‌خواهید به مرحله‌ی بعد برسید؟ سعی کنید از طریق یک آزمون و شبیه‌سازی ارسال یک فرم، کامنتی جدید را به همراه یک عکس از کنفرانس اضافه کنید. این کار به نظر بلندپروازانه می‌آید، اینطور نیست؟ به کد مورد نیاز نگاه کنید، پیچیده‌تر از چیزی که تا الان نوشته‌ایم نیست:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
--- a/tests/Controller/ConferenceControllerTest.php
+++ b/tests/Controller/ConferenceControllerTest.php
@@ -29,4 +29,19 @@ class ConferenceControllerTest extends WebTestCase
         $this->assertSelectorTextContains('h2', 'Amsterdam 2019');
         $this->assertSelectorExists('div:contains("There are 1 comments")');
     }
+
+    public function testCommentSubmission()
+    {
+        $client = static::createClient();
+        $client->request('GET', '/conference/amsterdam-2019');
+        $client->submitForm('Submit', [
+            'comment_form[author]' => 'Fabien',
+            'comment_form[text]' => 'Some feedback from an automated functional test',
+            'comment_form[email]' => '[email protected]',
+            'comment_form[photo]' => dirname(__DIR__, 2).'/public/images/under-construction.gif',
+        ]);
+        $this->assertResponseRedirects();
+        $client->followRedirect();
+        $this->assertSelectorExists('div:contains("There are 2 comments")');
+    }
 }

برای ارسال فرم از طریق submitForm()، اسم ورودی‌ها را به کمک ابزار‌های توسعه‌ای مرورگر یا از طریق پنل فرم در نمایه‌ساز سیمفونی، پیدا کنید. به بازاستفاده‌ی زیرکانه از تصویر دردست احداث توجه کنید!

برای بررسی سبز بودن همه چیز، آزمون‌ها را مجدداً اجرا کنید:

1
$ symfony php bin/phpunit tests/Controller/ConferenceControllerTest.php

یک مزیت استفاده از پایگاه‌داده‌ی «محیط توسعه» برای آزمون‌ها این است که می‌توانید نتایج را در مرورگر بررسی کنید:

بارگرفتن مجدد Fixtureها

اگر آزمون‌ها را برای بار دوم اجرا کنید، آن‌ها باید شکست بخورند. از آنجایی که در حال حاضر کامنت‌های بیشتری در پایگاه‌داده وجود دارد، ادعایی که تعداد کامنت‌ها را بررسی می‌کند، خراب شده است. ما نیاز داریم که قبل از هر اجرا، وضعیت پایگاه‌داده را از طریق بارگیری مجدد Fixtureها، بازتنظیم کنیم:

1
2
$ symfony console doctrine:fixtures:load
$ symfony php bin/phpunit tests/Controller/ConferenceControllerTest.php

خودکارسازی جریان‌کارتان از طریق یک Makefile

اجبار در به خاطر سپردن دنباله‌ای از فرامین برای اجرای آزمون‌ها، آزاردهنده است. این موضوع حداقل باید مستند شود. اما مستندسازی باید آخرین گزینه‌ باشد. به جای اینکار، نظرتان در مورد خودکارسازی فعالیت‌های روزمره چیست؟ این روش می‌تواند کار مستندسازی را انجام داده، به کشف این موضوع توسط سایر توسعه‌دهندگان کمک کرده و زندگی توسعه‌دهندگان را ساده‌تر و سریع‌تر کند.

استفاده از Makefile` یک راه برای خودکارسازی فرامین است:

Makefile
1
2
3
4
5
6
SHELL := /bin/bash

tests:
    symfony console doctrine:fixtures:load -n
    symfony php bin/phpunit
.PHONY: tests

به پرچم -n در فرمان Doctrine توجه کنید؛ این یک پرچم جهانی برای فرمان‌های سیمفونی است که آن‌ها را غیر تعاملی می‌کند.

هر زمان که می‌خواهید آزمون‌ها را اجرا کنید، از make tests استفاده کنید:

1
$ make tests

بازتنظیم پایگاه‌داده پس از هر آزمون

بازتنظیم پایگاه‌داده پس از هربار اجرای آزمون خوب است، اما داشتن آزمون‌های واقعاً مستقل از آن هم بهتر است. ما نمی‌خواهیم که یک آزمون وابسته به نتایج آزمون‌های قبلی باشد. تغییر ترتیب اجرای آزمون‌ها نباید حاصل کار را تغییر دهد. همانطور که خواهیم دید، در حال حاضر اینگونه نیست.

آزمون testConferencePage را پس از آزمون testCommentSubmission قرار دهید:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
--- a/tests/Controller/ConferenceControllerTest.php
+++ b/tests/Controller/ConferenceControllerTest.php
@@ -15,21 +15,6 @@ class ConferenceControllerTest extends WebTestCase
         $this->assertSelectorTextContains('h2', 'Give your feedback');
     }

-    public function testConferencePage()
-    {
-        $client = static::createClient();
-        $crawler = $client->request('GET', '/');
-
-        $this->assertCount(2, $crawler->filter('h4'));
-
-        $client->clickLink('View');
-
-        $this->assertPageTitleContains('Amsterdam');
-        $this->assertResponseIsSuccessful();
-        $this->assertSelectorTextContains('h2', 'Amsterdam 2019');
-        $this->assertSelectorExists('div:contains("There are 1 comments")');
-    }
-
     public function testCommentSubmission()
     {
         $client = static::createClient();
@@ -44,4 +29,19 @@ class ConferenceControllerTest extends WebTestCase
         $crawler = $client->followRedirect();
         $this->assertSelectorExists('div:contains("There are 2 comments")');
     }
+
+    public function testConferencePage()
+    {
+        $client = static::createClient();
+        $crawler = $client->request('GET', '/');
+
+        $this->assertCount(2, $crawler->filter('h4'));
+
+        $client->clickLink('View');
+
+        $this->assertPageTitleContains('Amsterdam');
+        $this->assertResponseIsSuccessful();
+        $this->assertSelectorTextContains('h2', 'Amsterdam 2019');
+        $this->assertSelectorExists('div:contains("There are 1 comments")');
+    }
 }

حالا آزمون‌ها شکست می‌خورند.

برای بازتنظیم پایگاه‌داده میان آزمون‌ها، DoctrineTestBundle را نصب کنید:

1
$ symfony composer require dama/doctrine-test-bundle --dev

نیاز دارید که اجرای recipe را تأیید کنید (زیرا این یک باندل با پشتیبانی «رسمی» نیست):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Symfony operations: 1 recipe (d7f110145ba9f62430d1ad64d57ab069)
  -  WARNING  dama/doctrine-test-bundle (>=4.0): From github.com/symfony/recipes-contrib:master
    The recipe for this package comes from the "contrib" repository, which is open to community contributions.
    Review the recipe at https://github.com/symfony/recipes-contrib/tree/master/dama/doctrine-test-bundle/4.0

    Do you want to execute this recipe?
    [y] Yes
    [n] No
    [a] Yes for all packages, only for the current installation session
    [p] Yes permanently, never ask again for this project
    (defaults to n): p

شنونده‌ی PHPUnit را فعال کنید:

patch_file
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -27,6 +27,10 @@
         </whitelist>
     </filter>

+    <extensions>
+        <extension class="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension" />
+    </extensions>
+
     <listeners>
         <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
     </listeners>

و تمام. هر تغییری که در طول اجرای آزمون انجام شود، پس از پایان هر آزمون به صورت خودکار به حالت اول برمی‌گردد.

آزمون‌ها باید مجدداً سبز باشند:

1
$ make tests

استفاده از یک مرورگر واقعی برای آزمون‌های کارکردی

آزمون‌های کارکردی، از یک مرورگر ویژه که لایه‌ی سیمفونی را مستقیماً فراخوانی می‌کند، استفاده می‌کنند. اما شما به لطف سیمفونی Panther، می‌توانید از یک مرورگر و لایه‌ی HTTP واقعی استفاده کنید:

هشدار

زمانی که این پاراگراف را نوشتم، نصب Panther بر روی سیمفونی ۵ امکان‌پذیر نبود زیرا که یکی از وابستگی‌ها هنوز سازگاری نداشت.

1
$ symfony composer req panther --dev

پس از این شما با انجام تغییرات زیر می‌توانید آزمون‌هایی بنویسید که از یک مرورگر گوگل کروم واقعی استفاده می‌کنند:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
--- a/tests/Controller/ConferenceControllerTest.php
+++ b/tests/Controller/ConferenceControllerTest.php
@@ -2,13 +2,13 @@

 namespace App\Tests\Controller;

-use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+use Symfony\Component\Panther\PantherTestCase;

-class ConferenceControllerTest extends WebTestCase
+class ConferenceControllerTest extends PantherTestCase
 {
     public function testIndex()
     {
-        $client = static::createClient();
+        $client = static::createPantherClient(['external_base_uri' => $_SERVER['SYMFONY_DEFAULT_ROUTE_URL']]);
         $client->request('GET', '/');

         $this->assertResponseIsSuccessful();

متغیر محیط SYMFONY_DEFAULT_ROUTE_URL، حاوی URL مربوط به وب سرور محلی است.

اجرای آزمون‌های کارکردی جعبه سیاه با Blackfire

یکی دیگر از راه‌های اجرای آزمون‌های کارکردی، استفاده از Blackfire player است. علاوه بر کارهایی که می‌توانید با آزمون‌های کارکردی انجام دهید، این روش می‌تواند آزمون‌های کارایی (performance tests) را نیز انجام دهد.

برای یادگیری بیشتر، به گام مربوط به «کارایی (Performance)» رجوع کنید.


  • « Previous گام 16: جلوگیری از ارسال محتوای هرز (Spam) با کمک یک API
  • Next » گام 18: پیش به سوی ناهمزمانی (Async)

This work, including the code samples, is licensed under a Creative Commons BY-NC-SA 4.0 license.