Intereting Posts

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

Я пытаюсь проверить класс интерфейса веб-сервиса с помощью PHPUnit. В принципе, этот класс вызывает вызовы объекта SoapClient . Я getMockFromWsdl проверить этот класс в PHPUnit с getMockFromWsdl метода 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); } 

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

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

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

Для базового использования будет работать что-то подобное. 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); } ?> 

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

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

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

Добавляю свои $ .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 из статической функции и получение доступа к функции getMockFromWsdl путем передачи в $this в качестве параметра – лучший способ выполнить это, но там вы идете.

Один из способов передать объект из теста для тестирования в 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; } } ?> 

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); } 

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