سؤال

بالنسبة للأشياء التي تنشأ كائن آخر كجزء من تنفيذها، ما هي أفضل طريقة لكتابة اختبار الوحدة حتى يتم اختبار كائن المبدأ فقط؟ مثال تافهة:

class myObj { 
    public function doSomethingWhichIsLogged()
    {
        // ...
        $logger = new logger('/tmp/log.txt');
        $logger->info('some message');
        // ...
    }
}

أعلم أن الكائن يمكن تصميمه بحيث يمكن حقن اعتماد كائن المسجل والآثار المخراج في اختبار الوحدة، ولكن هذا ليس دائما الحالة - في سيناريوهات أكثر تعقيدا، تحتاج إلى إنشاء كائنات أخرى أو إجراء مكالمات إلى أساليب ثابتة وبعد

نظرا لأننا لا نريد اختبار كائن مسجل، فقط Myobj، كيف نستمر؟ هل نقوم بإنشاء "مزدوج" طوابق مع برنامج نصي الاختبار؟ شيء مثل:

class logger
{
    public function __construct($filepath) {}
    public function info($message) {}
}

class TestMyObj extends PHPUnit_Framework_TestCase 
{
    // ...
}

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

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

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

المحلول

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

تحتوي بعض اللغات على أدوات يمكنها حقن فصول وهمية إلى التعليمات البرمجية، لكنني لا أعرف أي شيء مثل هذا ل PHP. في معظم الحالات، من المحتمل أن تكون أفضل حالا لإعادة صبط التعليمات البرمجية على أي حال.

نصائح أخرى

التالي troelskn. المشورة هنا مثال أساسي لما يجب عليك فعله.

<?php

class MyObj
{
    /**
     * @var LoggerInterface
     */
    protected $_logger;

    public function doSomethingWhichIsLogged()
    {
        // ...
        $this->getLogger()->info('some message');
        // ...
    }

    public function setLogger(LoggerInterface $logger)
    {
        $this->_logger = $logger;
    }

    public function getLogger()
    {
        return $this->_logger;
    }
}


class MyObjText extends PHPUnit_Framework_TestCase
{
    /**
     * @var MyObj
     */
    protected $_myObj;

    public function setUp()
    {
        $this->_myObj = new MyObj;
    }

    public function testDoSomethingWhichIsLogged()
    {
        $mockedMethods = array('info');
        $mock = $this->getMock('LoggerInterface', $mockedMethods);
        $mock->expects($this->any())
             ->method('info')
             ->will($this->returnValue(null));

        $this->_myObj->setLogger($mock);

        // do your testing
    }
}

يمكن العثور على مزيد من المعلومات حول كائنات وهمية في الدليل.

يبدو أنني أسيء فهم السؤال، واسمحوا لي أن أحاول مرة أخرى:

يجب عليك استخدام نمط Singleton أو مصنع للمسجل، إذا لم يفت الأوان بعد:

class LoggerStub extends Logger {
    public function info() {}
}
Logger::setInstance(new LoggerStub());
...
$logger = Logger::getInstance();

إذا لم تتمكن من تغيير التعليمات البرمجية، فيمكنك استخدام فئة Catch-All هي التحميل الزائد __يتصل()

class GenericStub {
    public function __call($functionName, $arguments) {}
}

يوجد بالفعل امتداد جديد معقول لتحميل فئة PHP الصادرة عن نفس اللاعبين الذين يبنون phpunit. يتيح لك تجاوز المشغل الجديد في الحالات التي لا يمكنك فيها إعادة تكوين التعليمات البرمجية، لسوء الحظ، ليس بهذا السهل التثبيت على Windows.

عنوان URL هو http://github.com/johannes/php-test-helpers/blob/master/

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