Ярлыки

.htaccess (4) тестирование (8) шаблоны проектирования (3) css (5) Debian (6) docker (2) Doctrine2 (6) Git (6) html (4) java (6) javascript (13) jquery (11) LFS (3) linux (23) mac os (4) mod_rewrite (2) MSSQL (4) MySQL (18) ORM Doctrine (17) patterns (3) PDO (3) perl (7) PHP (64) PHPUnit (8) Python (15) SEO (2) Silex (1) SimpleXML (1) SQL (14) ssh (4) Ubuntu (24) Yii1 (1) Zend Framework (19) ZendFramework2 (8)

пятница, 15 октября 2010 г.

PHPUnit.Тестирование кода, использующего Singletone.

Для начала посмотрим реализацию шаблона Singletone на php:
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}

Этот шаблон гарантирует, что код не будет доступен в приложении, иначе как через метод getInstance(). И клиент не сможет создать объект используя методы new или __clone.

class Client
{
    public function doSomething()
    {
        $singleton = Singleton::getInstance();
 
        // ...
    }
}

Очевидно, что невозможно написать тест для метода doSomething() без вызова getInstance(). Это значит, что мы не сможем создать новый объект для каждого из группы тестов и мы не сможем избежать побочных эффектов при тестировании.
Ниже приведены три варианта решения задачи:

Внедрение зависимости (Dependency Injection).

class Client
{
    public function doSomething(Singleton $singleton = NULL)
    {
        if ($singleton === NULL) {
            $singleton = Singleton::getInstance();
        }
 
        // ...
    }
}

Теперь вместо того, чтобы вызывать метод getInstance() мы можем подменить объект
класса Singletone mock-объектом или заглушкой (Stub).

class ClientTest extends PHPUnit_Framework_TestCase
{
    public function testSingleton()
    {
        $singleton = $this->getMock(
          'Singleton', /* name of class to mock     */
          array(),     /* list of methods to mock   */
          array(),     /* constructor arguments     */
          '',          /* name for mocked class     */
          FALSE        /* do not invoke constructor */
        );
 
        // ... configure $singleton ...
 
        $client = new Client;
        $client->doSomething($singleton);
 
        // ...
    }
}

Альтернативная реализация Singletone.

Вместо того чтобы модифицировать клиентский код для облегчения тестирования, мы может немного изменить реализацию одиночки, добавив дополнительный метод для обнуления объекта.
class Singleton
{
    private static $uniqueInstance = NULL;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
 
    public static function reset() {
        self::$uniqueInstance = NULL;
    }
}

Singleton с тестовым контекстом.

class Singleton
{
    private static $uniqueInstance = NULL;
    public static $testing = FALSE;
 
    protected function __construct() {}
    private final function __clone() {}
 
    public static function getInstance()
    {
        if (self::$uniqueInstance === NULL ||
            self::$testing) {
            self::$uniqueInstance = new Singleton;
        }
 
        return self::$uniqueInstance;
    }
}

Выставив Singleton::$testing = TRUE; мы гарантируем, что метод getInstance() будет создавать новый объект при каждом вызове.

Выдержка из публикации Sebastian Bergmann

Комментариев нет:

Отправить комментарий