Вопрос

Для объектов, которые составляют другой объект в рамках своей реализации, как лучше всего написать модульный тест, чтобы тестировался только основной объект?Тривиальный пример:

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 
{
    // ...
}

Это кажется возможным для небольших объектов, но было бы проблемой для более сложных API, где SUT зависела от возвращаемых значений.Кроме того, что, если вы хотите протестировать вызовы объекта зависимости так же, как и с фиктивными объектами?Есть ли способ имитировать объекты, экземпляры которых создаются SUT, а не передаются?

Я прочитал справочную страницу по макетам, но, похоже, она не охватывает ситуацию, когда зависимость формируется, а не агрегируется.Как ты делаешь это?

Это было полезно?

Решение

Как вы, кажется, уже знаете, конкретные зависимости классов делают тестирование трудным (или совершенно невозможным).Вам нужно отделить эту зависимость.Простое изменение, которое не нарушает существующий API, заключается в том, чтобы по умолчанию использовать текущее поведение, но предоставить возможность переопределить его.Есть несколько способов реализовать это.

В некоторых языках есть инструменты, которые могут внедрять в код фиктивные классы, но я не знаю ничего подобного для PHP.В большинстве случаев вам, вероятно, все равно будет лучше провести рефакторинг вашего кода.

Другие советы

Следующий Троэльскн посоветуйте вот базовый пример того, что вам следует делать.

<?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
    }
}

Дополнительную информацию о макетах объектов можно найти в руководстве.

Похоже, я неправильно понял вопрос, попробую еще раз:

Вам следует использовать шаблон синглтона или фабрику для регистратора, если еще не поздно:

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

Если вы не можете изменить код, вы можете использовать универсальный класс, который перегружает __вызов()

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