Как издеваться над тестированием веб-службы в PHPUnit через несколько тестов?

Я пытаюсь протестировать класс интерфейса веб-сервиса, используя PHPUnit. В принципе, этот класс вызывает вызовы объекта SoapClient. Я пытаюсь протестировать этот класс в PHPUnit с помощью метода getMockFromWsdl, описанного здесь:

http://www.phpunit.de/manual/current/en/test-doubles.html#test-doubles.stubbing-and-mocking-web-services

Однако, поскольку я хочу протестировать несколько методов из этого же класса, каждый раз, когда я настраиваю объект, мне также нужно настроить mock-объект WSDL SoapClient. Это вызывает фатальную ошибку:

Fatal error: Cannot redeclare class xxxx in C:\web\php5\PEAR\PHPUnit\Framework\TestCase.php(1227) : eval()'d code on line 15

Как я могу использовать один и тот же mock-объект для нескольких тестов без необходимости регенерировать его из WSDL? Кажется, это проблема.

-

После того, как вас попросили опубликовать некоторый код, чтобы посмотреть, здесь метод установки в TestCase:

protected function setUp() {
    parent::setUp();

    $this->client = new Client();

    $this->SoapClient = $this->getMockFromWsdl(
        'service.wsdl'
    );

    $this->client->setClient($this->SoapClient);
}

Ответ 1

Это не идеальное решение, но в вашей настройке SOAP издевается над "случайным" именем класса, например

$this->_soapClient = $this->getMockFromWsdl( 'some.wsdl', 'SoapClient' . md5( time().rand() ) );

Это гарантирует, что по крайней мере при вызове установки вы не получите эту ошибку.

Ответ 2

Для базового использования будет работать что-то подобное. PHPUnit делает магию за кулисами. Если вы кешируете макет-объект, он не будет обновлен. Просто создайте новую копию из этого кэшированного экземпляра, и вам должно быть хорошо идти.

<?php
protected function setUp() {
    parent::setUp();

    static $soapStub = null; // cache the mock object here (or anywhere else)
    if ($soapStub === null)
        $soapStub = $this->getMockFromWsdl('service.wsdl');

    $this->client = new Client;
    $this->client->setClient(clone $soapStub); // clone creates a new copy
}
?>

В качестве альтернативы вы, возможно, можете кэшировать имя класса с помощью get_class, а затем создать новый экземпляр, а не копию. Я не уверен, сколько "магии" PHPUnit делает для инициализации, но это стоит того.

<?php
protected function setUp() {
    parent::setUp();

    static $soapStubClass = null; // cache the mock object class' name
    if ($soapStubClass === null)
        $soapStubClass = get_class($this->getMockFromWsdl('service.wsdl'));

    $this->client = new Client;
    $this->client->setClient(new $soapStubClass);
}
?>

Ответ 3

Почему вы создаете макет в setUp(), если точка должна определить определение класса mock один раз за выполнение всего тестового файла? Если я правильно помню, он запускается перед каждым тестом, определенным в классе тестирования "this"... Попробуйте setUpBeforeClass()

От http://www.phpunit.de/manual/3.4/en/fixtures.html

Кроме того, вызываются методы шаблона setUpBeforeClass() и tearDownAfterClass() до того, как будет запущен первый тест класса тестового случая, и после запуска последнего теста класса тестового случая.

Ответ 4

Добавление моих $.02 здесь. Недавно я столкнулся с этой ситуацией и после некоторого разочарования вот как я смог ее решить:

class MyTest extends PHPUnit_Framework_TestCase
    protected static $_soapMock = null;

    public function testDoWork_WillSucceed( )
    {
        $this->_worker->setClient( self::getSoapClient( $this ) );
        $result = $this->_worker->doWork( );
        $this->assertEquals( true, $result['success'] );
    }

    protected static function getSoapClient( $obj )
    {
        if( !self::$_soapMock ) {
            self::$_soapMock = $obj->getMockFromWsdl( 
                'Test/wsdl.xml', 'SoapClient_MyWorker'
            );
        }

        return self::$_soapMock;
    }
}

У меня много "рабочих", каждый в своем собственном тестовом классе, и каждый из них нуждается в доступе к издеваемому SOAP-объекту. Во втором параметре getMockFromWsdl мне пришлось убедиться, что я передаю каждому уникальное имя (например, SoapClient_MyWorker), или это приведет к сбою PHPUnit.

Я не уверен, что SOAP mock из статической функции и получить доступ к функции getMockFromWsdl, перейдя в $this, поскольку параметр - лучший способ выполнить это, но там ya go.

Ответ 5

Один из способов передать объект из теста для тестирования в PHPUnits зависит от тестовой зависимости, если экземпляр определенного объекта слишком обременен/занимает много времени:

<?php
/**
* Pass an object from test to test
*/
class WebSericeTest extends PHPUnit_Framework_TestCase
{

    protected function setUp() {
        parent::setUp();
        // I don't know enough about your test cases, and do not know
        // the implications of moving code out of your setup.
    }

    /**
    * First Test which creates the SoapClient mock object.
    */
    public function test1()
    {
        $this->client = new Client();

        $soapClient = $this->getMockFromWsdl(
            'service.wsdl'
        );

        $this->client->setClient($this->SoapClient);
        $this->markTestIncomplete();
        // To complete this test you could assert that the
        // soap client is set in the client object. Or
        // perform some other test of your choosing.

        return $soapClient;
    }

    /**
    * Second Test depends on web service mock object from the first test.
    * @depends test1
    */
    public function test1( $soapClient )
    {
        // you should now have the soap client returned from the first test.
        return $soapClient;
    }

    /**
    * Third Test depends on web service mock object from the first test.
    * @depends test1
    */
    public function test3( $soapClient )
    {
        // you should now have the soap client returned from the first test.
        return $soapClient;
    }
}
?>

Ответ 6

PHPUnit создает класс для макета на основе WSDL. Имя класса, если оно не указано, построено из имени файла .wsdl, поэтому оно всегда одно и то же. Во всех тестах, когда он пытается снова создать класс, он падает.

Единственное, что вам нужно - добавить в определение Mock собственное имя, поэтому PHPUnit не создает автоматическое имя, обратите внимание на второй аргумент $this- > getMockFromWsdl:

protected function setUp() {
   parent::setUp();

   $this->client = new Client();

   $this->SoapClient = $this->getMockFromWsdl(
      'service.wsdl', 'MyMockClass'
   );

   $this->client->setClient($this->SoapClient);
}

Теперь вы можете создать столько клиентов, сколько хотите, только изменить имя класса для каждого.