Как издеваться над тестированием веб-службы в 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);
}
Теперь вы можете создать столько клиентов, сколько хотите, только изменить имя класса для каждого.