# Testing WebServices The same way we tested a web site, Codeception allows you to test web services. They are very hard to test manually, so it's a really good idea to automate web service testing. We have SOAP and REST as standards, which are represented in corresponding modules, which we will cover in this chapter. You should start by creating a new test suite, (which was not provided by the `bootstrap` command). We recommend calling it **api** and using the `ApiTester` class for it. ```bash $ php vendor/bin/codecept generate:suite api ``` We will put all the api tests there. ## REST The REST web service is accessed via HTTP with standard methods: `GET`, `POST`, `PUT`, `DELETE`. They allow users to receive and manipulate entities from the service. Accessing a WebService requires an HTTP client, so for using it you need the module `PhpBrowser` or one of framework modules set up. For example, we can use the `Symfony` module for Symfony2 applications in order to ignore web server and test web service internally. Configure modules in `api.suite.yml`: ``` yaml actor: ApiTester modules: enabled: - REST: url: http://serviceapp/api/v1/ depends: PhpBrowser ``` The REST module will connect to `PhpBrowser` according to this configuration. Depending on the web service we may deal with XML or JSON responses. Codeception handles both data formats well, however If you don't need one of them, you can explicitly specify that the JSON or XML parts of the module will be used: ``` yaml actor: ApiTester modules: enabled: - REST: url: http://serviceapp/api/v1/ depends: PhpBrowser part: Json ``` API tests can be functional and be executed using Symfony, Laravel5, Zend, or any other framework module. You will need slightly update configuration for it: ``` yaml actor: ApiTester modules: enabled: - REST: url: /api/v1/ depends: Laravel5 ``` Once we have configured our new testing suite, we can create the first sample test: ```bash $ codecept generate:cest api CreateUser ``` It will be called `CreateUserCest.php`. We need to implement a public method for each test. Let's make `createUserViaAPI` to test creation of a user via the REST API. ```php amHttpAuthenticated('service_user', '123456'); $I->haveHttpHeader('Content-Type', 'application/x-www-form-urlencoded'); $I->sendPOST('/users', ['name' => 'davert', 'email' => 'davert@codeception.com']); $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200 $I->seeResponseIsJson(); $I->seeResponseContains('{"result":"ok"}'); } } ``` We can use HTTP code constants from `Codeception\Util\HttpCode` instead of numeric values to check response code in `seeResponseCodeIs` and `dontSeeResponseCodeIs` methods. ### Testing JSON Responses The last line of the previous example verified that the response contained the provided string. However we shouldn't rely on it, as depending on content formatting we can receive different results with the same data. What we actually need is to check that the response can be parsed and it contains some of the values we expect. In the case of JSON we can use the `seeResponseContainsJson` method ``` php seeResponseContainsJson(['result' => 'ok']); // it can match tree-like structures as well $I->seeResponseContainsJson([ 'user' => [ 'name' => 'davert', 'email' => 'davert@codeception.com', 'status' => 'inactive' ] ]); ``` You may want to perform even more complex assertions on a response. This can be done by writing your own methods in the [Helper](http://codeception.com/docs/06-ReusingTestCode#Modules-and-Helpers) classes. To access the latest JSON response you will need to get the `response` property of the `REST` module. Let's demonstrate it with the `seeResponseIsHtml` method: ```php getModule('REST')->response; $this->assertRegExp('~^.*?<\/html>~m', $response); } } ``` The same way you can receive request parameters and headers. ### Validate JSON structures It is pretty common for API tests to not only validate the received data but to check the structure of the response. Response data is not usually considered to be consistent, and may change on each request, however the JSON/XML structure should be kept the same for an API version. In order to check response structure the REST module has some useful methods. If we expect a JSON response to be received we can check its structure with [JSONPath](http://goessner.net/articles/JsonPath/). It looks and sounds like XPath but is designed to work with JSON data, however we can convert JSON into XML and use XPath to validate the structure. Both approaches are valid and can be used in the REST module: ```php sendGET('/users'); $I->seeResponseCodeIs(HttpCode::OK); // 200 $I->seeResponseIsJson(); $I->seeResponseJsonMatchesJsonPath('$[0].user.login'); $I->seeResponseJsonMatchesXpath('//user/login'); ``` More detailed check can be applied if you need to validate the type of fields in a response. You can do that by using with a [seeResponseMatchesJsonType](http://codeception.com/docs/modules/REST#seeResponseMatchesJsonType) action in which you define the structure of JSON response. ```php sendGET('/users/1'); $I->seeResponseCodeIs(HttpCode::OK); // 200 $I->seeResponseIsJson(); $I->seeResponseMatchesJsonType([ 'id' => 'integer', 'name' => 'string', 'email' => 'string:email', 'homepage' => 'string:url|null', 'created_at' => 'string:date', 'is_active' => 'boolean' ]); ``` Codeception uses this simple and lightweight definitions format which can be [easily learned and extended](http://codeception.com/docs/modules/REST#seeResponseMatchesJsonType). ### Testing XML Responses In case your REST API works with XML format you can use similar methods to test its data and structure. There is `seeXmlResponseIncludes` method to match inclusion of XML parts in response, and `seeXmlResponseMatchesXpath` to validate its structure. ```php sendGET('/users.xml'); $I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200 $I->seeResponseIsXml(); $I->seeXmlResponseMatchesXpath('//user/login'); $I->seeXmlResponseIncludes(\Codeception\Util\Xml::toXml([ 'user' => [ 'name' => 'davert', 'email' => 'davert@codeception.com', 'status' => 'inactive' ] ])); ``` We are using `Codeception\Util\Xml` class which allows us to build XML structures in a clean manner. The `toXml` method may accept a string or array and returns \DOMDocument instance. If your XML contains attributes and so can't be represented as a PHP array you can create XML using the [XmlBuilder](http://codeception.com/docs/reference/XmlBuilder) class. We will take a look at it a bit more in next section.