Пересмешивание функций PHP в модульных тестах - PullRequest
11 голосов
/ 07 августа 2009

Я тестирую PHP-код с помощью SimpleTest, и у меня возникли проблемы. В моих тестах класса базы данных я хочу установить ожидание для функций PHP mysql. В моих тестах класса-оболочки для функции mail я хочу смоделировать функцию PHPs mail. Это всего лишь несколько примеров.

Суть в том, что я не хочу (всегда) проверять, отправляет ли мой класс Mail электронную почту, я хочу проверить, как он вызывает функцию mail. Я хочу иметь возможность контролировать, что возвращают эти функции. Я хочу иметь возможность тестировать мой класс базы данных, не требуя базы данных, приспособлений и всего этого.

У меня есть некоторый опыт тестирования кода на Ruby, а Test :: Unit и RSpec позволяют очень легко тестировать код изолированно. Я новичок в тестировании PHP, и мне кажется, что я тестирую намного больше, чем нужно для того, чтобы пройти мои тесты.

Есть ли способ в SimpleTest или PhpUnit или какой-либо другой среде тестирования, который делает это возможным или проще?

Ответы [ 4 ]

10 голосов
/ 08 августа 2009

Не в автоматическом режиме. Что вы можете сделать, это написать свой код таким образом, чтобы внешние зависимости были обернуты в объекты, которые передаются извне. В вашей производственной среде вы просто подключаете настоящие адаптеры, но во время тестирования вы можете подключить его к заглушкам или макетам.

Если вы действительно настаиваете, вы можете использовать расширение runkit , которое изменяет модель программирования php, чтобы вы могли переопределять классы и функции во время выполнения. Однако это внешнее и нестандартное расширение, так что имейте это в виду. Стандарт defacto - это ручной подход, как я описал выше.

1 голос
/ 13 апреля 2016

Вот интересная статья , в которой написано о насмешке над глобальными функциями php. Автор предлагает очень креативное решение для «Mock» (отменить) глобальных функций php путем перезаписи методов внутри пространства имен SUT (тестируемой службы).

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

<?php

namespace My\Namespace;

use PHPUnit_Framework_TestCase;

/**
 * Override time() in current namespace for testing
 *
 * @return int
 */
function time()
{
    return SomeClassTest::$now ?: \time();
}

class SomeClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @var int $now Timestamp that will be returned by time()
     */
    public static $now;

    /**
     * @var SomeClass $someClass Test subject
     */
    private $someClass;

    /**
     * Create test subject before test
     */
    protected function setUp()
    {
        parent::setUp();
        $this->someClass = new SomeClass;
    }

    /**
     * Reset custom time after test
     */
    protected function tearDown()
    {
        self::$now = null;
    }

    /*
     * Test cases
     */
    public function testOneHourAgoFromNoon()
    {
        self::$now = strtotime('12:00');
        $this->assertEquals('11:00', $this->someClass->oneHourAgo());
    }
    public function testOneHourAgoFromMidnight()
    {
        self::$now = strtotime('0:00');
        $this->assertEquals('23:00', $this->someClass->oneHourAgo());
    }
}

Не уверен, что это хороший способ сделать это, но он, безусловно, хорошо работает, и я думаю, что здесь стоит упомянуть. Может быть, немного еды для обсуждения ...

0 голосов
/ 31 августа 2015

В среде PHP 5.3+ вы можете обойти необходимость использования расширения runkit, взломав пространства имен. Единственное требование в том, что вызовы функций не используют полностью определенное пространство имен, как \mysql_query() - и обычно не используют. Затем вы можете определить ту же функцию в своем тесте, определив тест в пространстве имен, и PHP будет вызывать вашу функцию вместо глобальной. Лично я использую этот подход, чтобы заглушить вызов функции time(). Вот хороший пример с насмешливой структурой

0 голосов
/ 25 мая 2011

Существует расширение PHPUnit, которое использует runkit для внутреннего использования и способно использовать сопоставители вызовов, ограничения параметров и все, что может сделать макетированный объект.

https://github.com/tcz/phpunit-mockfunction

...