Мой фон Java. Я почему-то думал, что погрузиться в PHP будет весело.
У меня есть файл WSDL с несколькими определенными методами, которые мне нужно вызвать. Каждый метод обычно имеет один определенный запрос и один тип ответа. Все эти типы имеют один или два уровня глубины, без атрибутов — только элементы. Ничего фантастического. Почти все методы требуют некоторых общих аргументов, таких как «имя пользователя» и «пароль».
Я решил протестировать все это, и поэтому я создал косвенность, которая обрабатывает передачу стандартных аргументов. Производственный код выглядит примерно так:
class PaymentManager implements IPaymentManager {
public function __construct($soapClient, $username, $password, ...) {
$this->soapClient = $soapClient;
$this->username = $username;
...
}
public function chargeCustomer($price, $customerId) {
// prepare message
$message = new stdClass();
$message->ChargeMethodRequest = new stdClass();
$message->ChargeMethodRequest->Username = $this->username;
$message->ChargeMethodRequest->Password = $this->password;
$message->ChargeMethodRequest->Price = $price;
$message->ChargeMethodRequest->CustomerID = $customerId;
// make the actual call
$result = $this->soapClient->chargeMethod($message->ChargeMethodRequest);
// verify successful result
if ($result->ChargeMethodResponse->Result === "SUCCESS") {
throw new Exception("whopsie");
}
}
Теперь хитрость заключается в том, чтобы написать модульный тест для этого без необходимости использования реального экземпляра SoapClient. Я начал с SoapUI и сгенерировал примеры сообщений, но в файле PHP в виде статических строк, на которые я могу ссылаться из модульного теста. Итак, я представляю что-то вроде этого:
class WebServiceClientTest extends DrupalUnitTestCase /* yup, sad and true */ {
public function test_charge_method_happy_path() {
$soapClientMock = new SoapClientMock();
$testee = new WebServiceClient($soapClientMock, $un, $pw, ...);
// arrange
$successResponse = parseToStdClass(WebServiceClientMessages::RESPONSE_OK);
$expectedMessage = parseToStdClass(WebServiceClientMessages::REQUEST_EXAMPLE_1);
given($soapClientMock->chargeMethod($expectedMessage))->willReturn($successResponse);
// act
$testee->chargeCustomer("10.00", "customerId123");
// assert
verify($soapClientMock).chargeMethod($expectedMessage);
}
}
Первая попытка, Phockito: не удалась, так как SoapClient является «родным» классом, и его нельзя использовать с Reflection*
в PHP.
В противном случае я написал класс SoapClientMock
, в котором я хочу заглушить вызовы методов, а также проверить взаимодействие. Это не имеет большого значения, но проблема заключается в сопоставлении аргументов.
Как я могу взять примеры сообщений (строки XML), проанализировать их во что-то, что я могу сравнить с объектами stdClass, которые нужны SoapClient для правильной привязки материала? Насколько я понимаю, сравнение объектов жестко запрограммировано и проверяет, относятся ли два объекта к одному классу.
SimpleXMLElement был моей первой надеждой, но его не очень легко сравнивать с объектом stdClass, главным образом из-за того, как SimpleXMLElement хочет использовать пространства имен повсюду.
Сериализация не работает, поскольку объекты SimpleXMLElement являются «собственными» классами.
Кто-то предложил сделать json_decode(json_encode($object))
, а затем сравнить это, и это почти сработало, за исключением того, что SimpleXMLElement терпит неудачу, потому что нет способа получить дочерние узлы без указания пространства имен (и мне нужно использовать более одного), поэтому $expectedMessage
не содержит все элементы.
В настоящее время я пишу свой собственный парсер «xml string to stdClass», используя SAX.
Но подождите - все, что я хотел, это убедиться, что PaymentManager правильно заполняет полезную нагрузку для chargeMethod - почему я закончил тем, что написал парсер SAX.