تست نویسی در PHPUnit

آموزش PHPUnit

تاریخ : یکشنبه 12 آذر 1396

ساختار و نام فایل تست در PHP Unit

  • تمامی فایل های تست حتما باید با کلمه Test به پایان برسد. همچنین جهت آشکار سازی فایل تست که متعلق به کدام قسمت از برنامه است بهتر است فایل ها و مسیرهای همنام در دایرکتوری تست ایجاد گردند. برای مثال چنانچه در پروژه داشته باشیم :

    foo.php
    bar.php
    Controller/baz.php

    نام و مسیر فایل های تست ما بدینگونه خواهد بود :

    fooTest.php
    barTest.php
    Controller/bazTest.php

  • نام کلاس درون فایل تست بهتر است همنام با نام فایل باشد.
  • نام آزمون حتما باید با کلمه test بصورت حروف کوچک آغاز گردد.
  • اسامی آزمون ها باید توصیفی از عملی باشد که مورد آزمایش قرار می گیرد و بهتر است بصورت اختصار و کوتاه نباشد تا بیانگر آزمایشی باشد که انجام می دهد.

    برای مثال چنانچه تابعی به نام ()verifyAccount داشته باشیم که عمل مطابقت رمز عبور را برای یک حساب کاربری انجام می دهد آنگاه می توانیم نام آزمون را اینطور انتخاب کنیم :

    ()testVerifyAccountMatchesPasswordGiven

  • سطح دسترسی متدهای آزمون باید از نوع public باشند.
  • کلاس آزمون باید گسترش یافته از PHPUnit\Framework\TestCase و یا هرکلاس دیگری که این وظیفه را برعهده دارد باشد.

در کد زیر نمونه ای از یک آزمون را مشاهده می کنید :

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class StackTest extends TestCase
{
    public function testVerifyAccountMatchesPasswordGiven()
    {

    }
}

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


مثال 1: تست عملیات آرایه با PHPUnit

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class StackTest extends TestCase
{
    public function testPushAndPop()
    {
        $stack = [];
        $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}


نوشتن Test Dependencies (آزمون های وابسته به یکدیگر)

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

مثال 2.2 نشان می دهد که چگونه از استفاده از متن @depends تعریف می کند تا وابستگی بین روش های تست را بیان کند.


مثال 2 : استفاده از [email protected] در حاشیه نویسی جهت بیان وابستگی ها

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    public function testEmpty()
    {
        $stack = [];
        $this->assertEmpty($stack);

        return $stack;
    }

    /**
     * @depends testEmpty
     */
    public function testPush(array $stack)
    {
        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertNotEmpty($stack);

        return $stack;
    }

    /**
     * @depends testPush
     */
    public function testPop(array $stack)
    {
        $this->assertEquals('foo', array_pop($stack));
        $this->assertEmpty($stack);
    }
}

در مثال بالا، اولین تست، ()testEmpty یک آرایه جدید ایجاد می کند و با استفاده از متد assertEmpty اظهار می کند که آن خالی است و سپس به عنوان نتیجه آن را برگرداند. تست دوم، ()testPush به ()testEmpty بستگی دارد و نتیجه آن آزمون وابسته را به عنوان آرگومان دریافت می کند. همچنین در نهایت ()testPop به ()testPush وابستگی دارد.


مثال 3: بهره گیری از وابستگی بین آزمایش ها

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class DependencyFailureTest extends TestCase
{
    public function testOne()
    {
        $this->assertTrue(false);
    }

    /**
     * @depends testOne
     */
    public function testTwo()
    {
    }
}

نتیجه خروجی :


phpunit --verbose DependencyFailureTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) DependencyFailureTest::testOne
Failed asserting that false is true.

/home/sb/DependencyFailureTest.php:6
There was 1 skipped test:

1) DependencyFailureTest::testTwo
This test depends on "DependencyFailureTest::testOne" to pass.


FAILURES!
Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.


مثال 4: تست با چندین وابستگی

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    public function testProducerFirst()
    {
        $this->assertTrue(true);
        return 'first';
    }

    public function testProducerSecond()
    {
        $this->assertTrue(true);
        return 'second';
    }

    /**
     * @depends testProducerFirst
     * @depends testProducerSecond
     */
    public function testConsumer()
    {
        $this->assertEquals(
            ['first', 'second'],
            func_get_args()
        );
    }
}

نتیجه خروجی :


phpunit --verbose MultipleDependenciesTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

...

Time: 0 seconds, Memory: 3.25Mb

OK (3 tests, 3 assertions)


مفهوم Data Providers

یک روش آزمون می تواند آرگومان های دلخواه را قبول کند. این آرگومان ها توسط dataProvider با استفاده از حاشیه نویسی [email protected] مشخص می شود.


مثال 5: با استفاده از یک dataProvider که آرایه ای از آرایه ها را باز می گرداند

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }

    public function additionProvider()
    {
        return [
            [0, 0, 0],
            [0, 1, 1],
            [1, 0, 1],
            [1, 1, 2]
        ];
    }
}

نتیجه خروجی :


phpunit DataTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) DataTest::testAdd with data set #3 (1, 1, 3)
Failed asserting that 2 matches expected 3.

/home/sb/DataTest.php:9

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.

هنگام استفاده از تعداد زیادی از مجموعه داده ها مفید است که هر یک را با کلید رشته به جای پیش فرض عددی نامگذاری کنید.


مثال 6: استفاده از dataProvider با مجموعه داده های نامگذاری شده

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }

    public function additionProvider()
    {
        return [
            'adding zeros'  => [0, 0, 0],
            'zero plus one' => [0, 1, 1],
            'one plus zero' => [1, 0, 1],
            'one plus one'  => [1, 1, 3]
        ];
    }
}

نتیجه خروجی :


phpunit DataTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) DataTest::testAdd with data set "one plus one" (1, 1, 3)
Failed asserting that 2 matches expected 3.

/home/sb/DataTest.php:9

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.


مثال 7: ترکیب depends وdataProvider در تست

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    public function provider()
    {
        return [['provider1'], ['provider2']];
    }

    public function testProducerFirst()
    {
        $this->assertTrue(true);
        return 'first';
    }

    public function testProducerSecond()
    {
        $this->assertTrue(true);
        return 'second';
    }

    /**
     * @depends testProducerFirst
     * @depends testProducerSecond
     * @dataProvider provider
     */
    public function testConsumer()
    {
        $this->assertEquals(
            ['provider1', 'first', 'second'],
            func_get_args()
        );
    }
}

نتیجه خروجی :


phpunit --verbose DependencyAndDataProviderComboTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 3.50Mb

There was 1 failure:

1) DependencyAndDataProviderComboTest::testConsumer with data set #1 ('provider2')
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
-    0 => 'provider1'
+    0 => 'provider2'
1 => 'first'
2 => 'second'
)

/home/sb/DependencyAndDataProviderComboTest.php:31

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.


تست Testing Exceptions

مثال زیر نشان می دهد که چگونه از ()expectException در تست برای اینکه کدام استثنا رخ داده است استفاده می شود.


مثال 8: نحوه استفاده از متد expectException

<?php
use PHPUnit\Framework\TestCase;

class ExceptionTest extends TestCase
{
    public function testException()
    {
        $this->expectException(InvalidArgumentException::class);
    }
}

نتیجه خروجی :


phpunit ExceptionTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ExceptionTest::testException
Expected exception InvalidArgumentException

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

در ادامه متد ()expectException، متدهای ()expectExceptionCode و ()expectExceptionMessage و ()expectExceptionMessageRegExp نیز جهت بررسی استثنائات مطرح شده توسط کد تحت آزمون مورد استفاده قرار می گیرند.


مثال 9: نحوه استفاده از حاشیه نویسی [email protected]

<?php
    /**
     * @expectedException InvalidArgumentException
     */
    public function testException()
    {
    }

نتیجه خروجی :


phpunit ExceptionTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ExceptionTest::testException
Expected exception InvalidArgumentException

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.


تست PHP Errors

به طور پیش فرض، PHPUnit اعلان های errors, warnings, notices که در هنگام اجرای یک آزمون ایجاد می شوند را به به یک استثنا تبدیل می کند.


مثال 10:انتظار برای نمایش PHP error با استفاده از [email protected]

<?php
use PHPUnit\Framework\TestCase;

class ExpectedErrorTest extends TestCase
{
    /**
     * @expectedException PHPUnit\Framework\Error
     */
    public function testFailingInclude()
    {
        include 'not_existing_file.php';
    }
}

نتیجه خروجی :


phpunit -d error_reporting=2 ExpectedErrorTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

.

Time: 0 seconds, Memory: 5.25Mb

OK (1 test, 1 assertion)


مثال 11:تست مقدار بازگشتی کد که از خطاهای PHP استفاده می کند

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    public function testFileWriting() {
        $writer = new FileWriter;
        $this->assertFalse(@$writer->write('/is-not-writeable/file', 'stuff'));
    }
}

class FileWriter
{
    public function write($file, $content) {
        $file = fopen($file, 'w');
        if($file == false) {
            return false;
        }
        // ...
    }
}

نتیجه خروجی :


phpunit ErrorSuppressionTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

.

Time: 1 seconds, Memory: 5.25Mb

OK (1 test, 1 assertion)


تست Output

گاهی اوقات شما می خواهید صحت اجرای یک روش، به عنوان مثال، یک خروجی مورد انتظار را بررسی کنید (به عنوان مثال از طریق echo یا print)


مثال 12:تست خروجی یک تابع یا متد

<?php
namespace Test;

use PHPUnit\Framework\TestCase;

class Examples extends TestCase
{
    public function testExpectFooActualFoo()
    {
        $this->expectOutputString('foo');
        print 'foo';
    }

    public function testExpectBarActualBaz()
    {
        $this->expectOutputString('bar');
        print 'baz';
    }
}

نتیجه خروجی :


phpunit OutputTest
PHPUnit 6.5.0 by Sebastian Bergmann and contributors.

.F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) OutputTest::testExpectBarActualBaz
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'


FAILURES!
Tests: 2, Assertions: 2, Failures: 1.


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

Method Meaning
void expectOutputRegex(string $regularExpression) Set up the expectation that the output matches a $regularExpression.
void expectOutputString(string $expectedString) Set up the expectation that the output is equal to an $expectedString.
bool setOutputCallback(callable $callback) Sets up a callback that is used to, for instance, normalize the actual output.
string getActualOutput() Get the actual output.

منابع مورد مطالعه جهت جمع آوری این مطلب:
https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.exceptions

نظرات