Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00%
0 / 1
0.00%
0 / 21
CRAP
0.00%
0 / 202
SOAP
0.00%
0 / 1
0.00%
0 / 21
2256
0.00%
0 / 202
 _before(\Codeception\TestCase $test)
0.00%
0 / 1
56
0.00%
0 / 21
 haveSoapHeader($header, $params = array()
0.00%
0 / 1
2
0.00%
0 / 9
 sendSoapRequest($action, $body = "")
0.00%
0 / 1
42
0.00%
0 / 28
 seeSoapResponseEquals($xml)
0.00%
0 / 1
2
0.00%
0 / 5
 seeSoapResponseIncludes($xml)
0.00%
0 / 1
2
0.00%
0 / 5
 dontSeeSoapResponseEquals($xml)
0.00%
0 / 1
2
0.00%
0 / 5
 dontSeeSoapResponseIncludes($xml)
0.00%
0 / 1
2
0.00%
0 / 5
 seeSoapResponseContainsStructure($xml)
0.00%
0 / 1
12
0.00%
0 / 13
 seeSoapResponseContainsXPath($xpath)
0.00%
0 / 1
6
0.00%
0 / 7
 dontSeeSoapResponseContainsXPath($xpath)
0.00%
0 / 1
6
0.00%
0 / 7
 seeResponseCodeIs($code)
0.00%
0 / 1
2
0.00%
0 / 3
 grabTextContentFrom($cssOrXPath)
0.00%
0 / 1
2
0.00%
0 / 4
 grabAttributeFrom($cssOrXPath, $attribute)
0.00%
0 / 1
6
0.00%
0 / 5
 matchElement($cssOrXPath)
0.00%
0 / 1
20
0.00%
0 / 14
 structureMatches($schema, $xml)
0.00%
0 / 1
42
0.00%
0 / 14
 getSchema()
0.00%
0 / 1
2
0.00%
0 / 4
 canonicalize($xml)
0.00%
0 / 1
2
0.00%
0 / 5
 buildRequest()
0.00%
0 / 1
2
0.00%
0 / 15
 processRequest($action, $body)
0.00%
0 / 1
2
0.00%
0 / 12
 processInternalRequest($action, $body)
0.00%
0 / 1
12
0.00%
0 / 16
 processExternalRequest($action, $body)
0.00%
0 / 1
2
0.00%
0 / 5
<?php
namespace Codeception\Module;
/**
* Module for testing SOAP WSDL web services.
* Send requests and check if response matches the pattern.
*
* This module can be used either with frameworks or PHPBrowser.
* It tries to guess the framework is is attached to.
* If a endpoint is a full url then it uses PHPBrowser.
*
* ### Using Inside Framework
* Please note, that PHP SoapServer::handle method sends additional headers.
* This may trigger warning: "Cannot modify header information"
* If you use PHP SoapServer with framework, try to block call to this method in testing environment.
*
* ## Configuration
*
* * endpoint *required* - soap wsdl endpoint
*
* ## Public Properties
*
* * request - last soap request (DOMDocument)
* * response - last soap response (DOMDocument)
*
*/
use Codeception\Util\Soap as SoapUtils;
class SOAP extends \Codeception\Module
{
protected $config = array('schema' => "", 'schema_url' => 'http://schemas.xmlsoap.org/soap/envelope/');
protected $requiredFields = array('endpoint');
/**
* @var \Symfony\Component\BrowserKit\Client
*/
public $client = null;
public $is_functional = false;
/**
* @var \DOMDocument
*/
public $xmlRequest = null;
/**
* @var \DOMDocument
*/
public $xmlResponse = null;
public function _before(\Codeception\TestCase $test)
{
if (!$this->client) {
if (!strpos($this->config['endpoint'], '://')) {
// not valid url
foreach ($this->getModules() as $module) {
if ($module instanceof \Codeception\Util\Framework) {
$this->client = $module->client;
$this->is_functional = true;
break;
}
}
} else {
if (!$this->hasModule('PhpBrowser'))
throw new \Codeception\Exception\ModuleConfig(__CLASS__, "For Soap testing via HTTP please enable PhpBrowser module");
$this->client = $this->getModule('PhpBrowser')->session->getDriver()->getClient();
}
if (!$this->client) throw new \Codeception\Exception\ModuleConfig(__CLASS__, "Client for SOAP requests not initialized.\nProvide either PhpBrowser module or Framework module which shares FrameworkInterface");
}
$this->buildRequest();
$this->xmlResponse = null;
}
/**
* Prepare SOAP header.
* Receives header name and parameters as array.
*
* Example:
*
* ``` php
* <?php
* $I->haveSoapHeader('AuthHeader', array('username' => 'davert', 'password' => '123345'));
* ```
*
* Will produce header:
*
* ```
* <soapenv:Header>
* <SessionHeader>
* <AuthHeader>
* <username>davert</username>
* <password>12345</password>
* </AuthHeader>
* </soapenv:Header>
* ```
*
* @param $header
* @param array $params
*/
public function haveSoapHeader($header, $params = array())
{
$soap_schema_url = $this->config['schema_url'];
$xml = $this->xmlRequest;
$xmlHeader = $xml->documentElement->getElementsByTagNameNS($soap_schema_url, 'Header')->item(0);
$headerEl = $xml->createElement($header);
SoapUtils::arrayToXml($xml, $headerEl, $params);
$xmlHeader->appendChild($headerEl);
}
/**
* Submits request to endpoint.
*
* Requires of api function name and parameters.
* Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
*
* You are allowed to execute as much requests as you need inside test.
*
* Example:
*
* ``` php
* $I->sendRequest('UpdateUser', '<user><id>1</id><name>notdavert</name></user>');
* $I->sendRequest('UpdateUser', \Codeception\Utils\Soap::request()->user
* ->id->val(1)->parent()
* ->name->val('notdavert');
* ```
*
* @param $request
* @param $body
*/
public function sendSoapRequest($action, $body = "")
{
$soap_schema_url = $this->config['schema_url'];
$xml = $this->xmlRequest;
$call = $xml->createElement('ns:' . $action);
if ($body) {
$bodyXml = SoapUtils::toXml($body);
if ($bodyXml->hasChildNodes()) {
foreach ($bodyXml->childNodes as $bodyChildNode) {
$bodyNode = $xml->importNode($bodyChildNode, true);
$call->appendChild($bodyNode);
}
}
}
$xmlBody = $xml->getElementsByTagNameNS($soap_schema_url, 'Body')->item(0);
// cleanup if body already set
foreach ($xmlBody->childNodes as $node) {
$xmlBody->removeChild($node);
}
$xmlBody->appendChild($call);
$this->debugSection("Request", $req = $xml->C14N());
if ($this->is_functional) {
$response = $this->processInternalRequest($action, $req);
} else {
$response = $this->processExternalRequest($action, $req);
}
$this->debugSection("Response", $response);
$this->xmlResponse = SoapUtils::toXml($response);
}
/**
* Checks XML response equals provided XML.
* Comparison is done by canonicalizing both xml`s.
*
* Parameters can be passed either as DOMDocument, DOMNode, XML string, or array (if no attributes).
*
* Example:
*
* ``` php
* <?php
* $I->seeSoapResponseEquals("<?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope><SOAP-ENV:Body><result>1</result></SOAP-ENV:Envelope>");
*
* $dom = new \DOMDocument();
* $dom->load($file);
* $I->seeSoapRequestIncludes($dom);
*
* ```
*
* @param $xml
*/
public function seeSoapResponseEquals($xml)
{
$xml = SoapUtils::toXml($xml);
\PHPUnit_Framework_Assert::assertEquals($this->xmlResponse->C14N(), $xml->C14N());
}
/**
* Checks XML response includes provided XML.
* Comparison is done by canonicalizing both xml`s.
* Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
*
* Example:
*
* ``` php
* <?php
* $I->seeSoapResponseIncludes("<result>1</result>");
* $I->seeSoapRequestIncludes(\Codeception\Utils\Soap::response()->result->val(1));
*
* $dom = new \DDOMDocument();
* $dom->load('template.xml');
* $I->seeSoapRequestIncludes($dom);
* ?>
* ```
*
* @param $xml
*/
public function seeSoapResponseIncludes($xml)
{
$xml = $this->canonicalize($xml);
\PHPUnit_Framework_Assert::assertContains($xml, $this->xmlResponse->C14N(), "found in XML Response");
}
/**
* Checks XML response equals provided XML.
* Comparison is done by canonicalizing both xml`s.
*
* Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
*
* @param $xml
*/
public function dontSeeSoapResponseEquals($xml)
{
$xml = SoapUtils::toXml($xml);
\PHPUnit_Framework_Assert::assertXmlStringNotEqualsXmlString($this->xmlResponse->C14N(), $xml->C14N());
}
/**
* Checks XML response does not include provided XML.
* Comparison is done by canonicalizing both xml`s.
* Parameter can be passed either as XmlBuilder, DOMDocument, DOMNode, XML string, or array (if no attributes).
*
* @param $xml
*/
public function dontSeeSoapResponseIncludes($xml)
{
$xml = $this->canonicalize($xml);
\PHPUnit_Framework_Assert::assertNotContains($xml, $this->xmlResponse->C14N(), "found in XML Response");
}
/**
* Checks XML response contains provided structure.
* Response elements will be compared with XML provided.
* Only nodeNames are checked to see elements match.
*
* Example:
*
* ``` php
* <?php
*
* $I->seeResponseContains("<user><query>CreateUser<name>Davert</davert></user>");
* $I->seeSoapResponseContainsStructure("<query><name></name></query>");
* ?>
* ```
*
* Use this method to check XML of valid structure is returned.
* This method does not use schema for validation.
* This method does not require path from root to match the structure.
*
* @param $xml
*/
public function seeSoapResponseContainsStructure($xml) {
$xml = SoapUtils::toXml($xml);
$this->debugSection("Structure", $xml->saveXML());
$root = $xml->firstChild;
$this->debugSection("Structure Root", $root->nodeName);
$els = $this->xmlResponse->getElementsByTagName($root->nodeName);
if (empty($els)) return \PHPUnit_Framework_Assert::fail("Element {$root->nodeName} not found in response");
$matches = false;
foreach ($els as $node) {
$matches |= $this->structureMatches($root, $node);
}
\PHPUnit_Framework_Assert::assertTrue((bool)$matches, "this structure is in response");
}
/**
* Checks XML response with XPath locator
*
* ``` php
* <?php
* $I->seeSoapResponseContainsXPath('//root/user[@id=1]');
* ?>
* ```
*
* @param $xpath
*/
public function seeSoapResponseContainsXPath($xpath)
{
$path = new \DOMXPath($this->xmlResponse);
$res = $path->query($xpath);
if ($res === false) $this->fail("XPath selector is malformed");
$this->assertGreaterThen(0, $res->length);
}
/**
* Checks XML response doesn't contain XPath locator
*
* ``` php
* <?php
* $I->dontSeeSoapResponseContainsXPath('//root/user[@id=1]');
* ?>
* ```
*
* @param $xpath
*/
public function dontSeeSoapResponseContainsXPath($xpath)
{
$path = new \DOMXPath($this->xmlResponse);
$res = $path->query($xpath);
if ($res === false) $this->fail("XPath selector is malformed");
$this->assertEquals(0, $res->length);
}
/**
* Checks response code from server.
*
* @param $code
*/
public function seeResponseCodeIs($code) {
\PHPUnit_Framework_Assert::assertEquals($code, $this->client->getResponse()->getStatus(), "soap response code matches expected");
}
/**
* Finds and returns text contents of element.
* Element is matched by either CSS or XPath
*
* @version 1.1
* @param $cssOrXPath
* @return string
*/
public function grabTextContentFrom($cssOrXPath) {
$el = $this->matchElement($cssOrXPath);
return $el->textContent;
}
/**
* Finds and returns attribute of element.
* Element is matched by either CSS or XPath
*
* @version 1.1
* @param $cssOrXPath
* @param $attribute
* @return string
*/
public function grabAttributeFrom($cssOrXPath, $attribute) {
$el = $this->matchElement($cssOrXPath);
if (!$el->hasAttribute($attribute)) $this->fail("Attribute not found in element matched by '$cssOrXPath'");
return $el->getAttribute($attribute);
}
/**
* @param $cssOrXPath
* @return \DOMElement
*/
protected function matchElement($cssOrXPath)
{
$xpath = new \DOMXpath($this->xmlResponse);
try {
$selector = \Symfony\Component\CssSelector\CssSelector::toXPath($cssOrXPath);
$els = $xpath->query($selector);
if ($els) return $els->item(0);
} catch (\Symfony\Component\CssSelector\Exception\ParseException $e) {}
$els = $xpath->query($cssOrXPath);
if ($els) {
return $els->item(0);
}
$this->fail("No node matched CSS or XPath '$cssOrXPath'");
}
protected function structureMatches($schema, $xml)
{
foreach ($schema->childNodes as $node1) {
$matched = false;
foreach ($xml->childNodes as $node2) {
if ($node1->nodeName == $node2->nodeName) {
$matched = $this->structureMatches($node1, $node2);
if ($matched) break;
}
}
if (!$matched) return false;
}
return true;
}
protected function getSchema()
{
return $this->config['schema'];
}
protected function canonicalize($xml)
{
$xml = SoapUtils::toXml($xml)->C14N();
return $xml;
}
/**
* @return \DOMDocument
*/
protected function buildRequest()
{
$soap_schema_url = $this->config['schema_url'];
$xml = new \DOMDocument();
$root = $xml->createElement('soapenv:Envelope');
$xml->appendChild($root);
$root->setAttribute('xmlns:ns', $this->getSchema());
$root->setAttribute('xmlns:soapenv', $soap_schema_url);
$body = $xml->createElementNS($soap_schema_url, 'soapenv:Body');
$header = $xml->createElementNS($soap_schema_url, 'soapenv:Header');
$root->appendChild($header);
$root->appendChild($body);
$this->xmlRequest = $xml;
return $xml;
}
protected function processRequest($action, $body)
{
$this->client->request('POST',
$this->config['endpoint'],
array(), array(),
array(
"HTTP_Content-Type" => "text/xml; charset=UTF-8",
'HTTP_Content-Length' => strlen($body),
'HTTP_SOAPAction' => $action),
$body
);
}
protected function processInternalRequest($action, $body)
{
ob_start();
try {
$this->client->setServerParameter('HTTP_HOST', 'localhost');
$this->processRequest($action, $body);
} catch (\ErrorException $e) {
// Zend_Soap outputs warning as an exception
if (strpos($e->getMessage(),'Warning: Cannot modify header information')===false) {
ob_end_clean();
throw $e;
}
}
$response = ob_get_contents();
ob_end_clean();
return $response;
}
protected function processExternalRequest($action, $body)
{
$this->processRequest($action, $body);
return $this->client->getResponse()->getContent();
}
}