2009-08-07 5 views
10

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

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

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

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

ответ

10

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

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

+0

Я вижу. Я как бы ожидал (но не надеялся) такого ответа. Я действительно не настаиваю, так как хочу упростить и ускорить тестирование, а не сделать его более сложным. Спасибо за ваш ответ! – avdgaag

+0

Теперь [Mockery] (http://docs.mockery.io/en/latest/cookbook/mocking_hard_dependencies.html) кажется [способ пойти] (http://stackoverflow.com/a/42158443/659788). – Franco

+1

@Franco Не для встроенных. – troelskn

0

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

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

0

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

+0

Вы можете использовать [php-mock] (https: // github.com/php-mock/php-mock) для создания таких макетов. –

1

Here is an interesting article, который пишет о насмешливых глобальных функциях 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()); 
    } 
} 

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

Смежные вопросы