سؤال

هل هناك طريقة لإجراء الاختبارات داخل ملف TestCase لتشغيل في ترتيب معين؟على سبيل المثال، أريد فصل دورة حياة الكائن بدءًا من الإنشاء وحتى الاستخدام وحتى التدمير ولكنني بحاجة إلى التأكد من إعداد الكائن أولاً قبل إجراء الاختبارات الأخرى.

هل كانت مفيدة؟

المحلول

ربما هناك مشكلة في التصميم في اختباراتك.

عادةً، يجب ألا يعتمد كل اختبار على أي اختبارات أخرى، حتى يمكن إجراؤه بأي ترتيب.

يحتاج كل اختبار إلى إنشاء مثيل وتدمير كل ما يحتاجه لتشغيله، وهذا من شأنه أن يكون النهج المثالي، ويجب ألا تشارك أبدًا الكائنات والحالات بين الاختبارات.

هل يمكنك أن تكون أكثر تحديدًا بشأن سبب حاجتك إلى نفس الكائن في اختبارات N؟

نصائح أخرى

تدعم PHPUnit تبعيات الاختبار عبر ملف @يعتمد على حاشية. ملاحظة.

فيما يلي مثال من الوثائق حيث سيتم تشغيل الاختبارات بترتيب يلبي التبعيات، حيث يقوم كل اختبار تابع بتمرير وسيطة إلى الاختبار التالي:

class StackTest extends PHPUnit_Framework_TestCase
{
    public function testEmpty()
    {
        $stack = array();
        $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);
    }
}

ومع ذلك، من المهم ملاحظة أن الاختبارات ذات التبعيات التي لم يتم حلها ستفعل ذلك لا سيتم تنفيذه (أمر مرغوب فيه، لأن هذا يلفت الانتباه سريعًا إلى الاختبار الفاشل).لذلك، من المهم الانتباه جيدًا عند استخدام التبعيات.

الإجابة الصحيحة لهذا هو ملف التكوين المناسب للاختبارات.واجهت نفس المشكلة وقمت بإصلاحها عن طريق إنشاء مجموعة اختبارية مع ترتيب ملفات الاختبار الضرورية:

phpunit.xml:

<phpunit
        colors="true"
        bootstrap="./tests/bootstrap.php"
        convertErrorsToExceptions="true"
        convertNoticesToExceptions="true"
        convertWarningsToExceptions="true"
        strict="true"
        stopOnError="false"
        stopOnFailure="false"
        stopOnIncomplete="false"
        stopOnSkipped="false"
        stopOnRisky="false"
>
    <testsuites>
        <testsuite name="Your tests">
            <file>file1</file> //this will be run before file2
            <file>file2</file> //this depends on file1
        </testsuite>
    </testsuites>
</phpunit>

إذا كنت تريد أن تشارك اختباراتك العديد من الكائنات والإعدادات المساعدة، فيمكنك استخدام setUp(), tearDown() لإضافة إلى sharedFixture ملكية.

تسمح PHPUnit باستخدام التعليق التوضيحي "@depends" الذي يحدد حالات الاختبار التابعة ويسمح بتمرير الوسائط بين حالات الاختبار التابعة.

من وجهة نظري، خذ السيناريو التالي حيث أحتاج إلى اختبار إنشاء مورد معين وتدميره.

في البداية كان لدي طريقتين، أ.testCreateResource و ب.testDestroyResource

أ.testCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
?>

ب.testDestroyResource

<?php
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>

أعتقد أن هذه فكرة سيئة، حيث يعتمد testDestroyResource على testCreateResource.والممارسة الأفضل هي القيام بذلك

أ.testCreateResource

<?php
$app->createResource('resource');
$this->assertTrue($app->hasResource('resource'));
$app->deleteResource('resource');
?>

ب.testDestroyResource

<?php
$app->createResource('resource');
$app->destroyResource('resource');
$this->assertFalse($app->hasResource('resource'));
?>

حل بديل:استخدم الوظائف الثابتة (!) في اختباراتك لإنشاء عناصر قابلة لإعادة الاستخدام.على سبيل المثال (أستخدم السيلينيوم IDE لتسجيل الاختبارات وphpunit-selenium (github) لتشغيل الاختبار داخل المتصفح)

class LoginTest extends SeleniumClearTestCase
{
    public function testAdminLogin()
    {
        self::adminLogin($this);
    }

    public function testLogout()
    {
        self::adminLogin($this);
        self::logout($this);
    }

    public static function adminLogin($t)
    {
        self::login($t, 'john.smith@gmail.com', 'pAs$w0rd');
        $t->assertEquals('John Smith', $t->getText('css=span.hidden-xs'));
    }

    // @source LoginTest.se
    public static function login($t, $login, $pass)
    {
        $t->open('/');
        $t->click("xpath=(//a[contains(text(),'Log In')])[2]");
        $t->waitForPageToLoad('30000');
        $t->type('name=email', $login);
        $t->type('name=password', $pass);
        $t->click("//button[@type='submit']");
        $t->waitForPageToLoad('30000');
    }

    // @source LogoutTest.se
    public static function logout($t)
    {
        $t->click('css=span.hidden-xs');
        $t->click('link=Logout');
        $t->waitForPageToLoad('30000');
        $t->assertEquals('PANEL', $t->getText("xpath=(//a[contains(text(),'Panel')])[2]"));
    }
}

حسنًا، والآن، يمكنني استخدام هذه العناصر القابلة لإعادة الاستخدام في اختبار آخر :) على سبيل المثال:

class ChangeBlogTitleTest extends SeleniumClearTestCase
{
    public function testAddBlogTitle()
    {
      self::addBlogTitle($this,'I like my boobies');
      self::cleanAddBlogTitle();
    }

    public static function addBlogTitle($t,$title) {
      LoginTest::adminLogin($t);

      $t->click('link=ChangeTitle');
      ...
      $t->type('name=blog-title', $title);
      LoginTest::logout($t);
      LoginTest::login($t, 'paris@gmail.com','hilton');
      $t->screenshot(); // take some photos :)
      $t->assertEquals($title, $t->getText('...'));
    }

    public static function cleanAddBlogTitle() {
        $lastTitle = BlogTitlesHistory::orderBy('id')->first();
        $lastTitle->delete();
    }
  • بهذه الطريقة، يمكنك بناء تسلسل هرمي للاختبارات الخاصة بك.
  • يمكنك الاحتفاظ بخاصية أن كل حالة اختبار منفصلة تمامًا عن الأخرى (إذا قمت بتنظيف قاعدة البيانات بعد كل اختبار).
  • والأهم من ذلك، على سبيل المثال، إذا تغيرت طريقة تسجيل الدخول في المستقبل، فأنت تقوم فقط بتعديل فئة LoginTest، ولن تحتاج إلى جزء تسجيل الدخول الصحيح في الاختبارات الأخرى (يجب أن تعمل بعد تحديث LoginTest) :)

عندما أقوم بإجراء اختبار البرنامج النصي الخاص بي لتنظيف إعلان db في البداية.أعلاه أستخدم بلدي SeleniumClearTestCase فئة (أقوم بعمل لقطة شاشة () ووظائف لطيفة أخرى هناك) وهي امتداد لـ MigrationToSelenium2 (من github، إلى منفذ الاختبارات المسجلة في Firefox باستخدام المكوّن الإضافي seleniumIDE + ff "Selenium IDE:PHP Formatters" ) وهو امتداد لصفي LaravelTestCase (إنها نسخة من Illuminate\Foundation esting estCase ولكنها لا تمتد PHPUnit_Framework_TestCase) والتي تقوم بإعداد laravel للوصول إلى eloquent عندما نريد تنظيف قاعدة البيانات في نهاية الاختبار) وهو ملحق PHPUnit_Extensions_Selenium2TestCase.لإعداد laravel eloquent لدي أيضًا في وظيفة SeleniumClearTestCase createApplication (والتي تسمى في setUp, ، وأغتنم هذه الوظيفة من اختبار laral/TestCase)

هناك بالفعل مشكلة في اختباراتك إذا كانت بحاجة إلى التشغيل بترتيب معين.يجب أن يكون كل اختبار مستقلاً تمامًا عن الاختبارات الأخرى:فهو يساعدك في توطين العيوب، ويسمح لك بالحصول على نتائج قابلة للتكرار (وبالتالي قابلة للتصحيح).

الدفع هذا الموقع للحصول على مجموعة كاملة من الأفكار/المعلومات، حول كيفية تحليل اختباراتك بطريقة تتجنب فيها هذه الأنواع من المشكلات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top