Webサービスをテストする
CodeceptionはWebサイトのテストと同じ方法で、Webサービスをテストすることができます。Webサービスを手動でテストすることはとても大変なので、テストを自動化することはとても良いアイディアです。CodeceptionにはSOAPとRESTに対応したモジュールが標準で備えられています。この章ではそれらのモジュールについて説明します。
新しくテストスイートを作成するところからはじめましょう。これはbootstrap
コマンドでは提供されていません。テストスイートの名前はapiとし、ApiTester
クラスを使いましょう。
$ php codecept generate:suite api
ここにAPIのテストを記述していきます。
REST
REST方式のWebサービスは、HTTPの標準的なメソッドであるGET
、POST
、PUT
、DELETE
を介してアクセスされます。これにより、ユーザーはWebサービスからエンティティを受け取り、操作することができます。WebサービスへのアクセスにはHTTPクライアントが必要であるため、PhpBrowser
やいずれかのフレームワーク用モジュールのセットアップを行う必要があります。Webサーバーを無視し、Webサービスを内部的にテストするために、たとえば、Symfony2で実装されたアプリケーションであれば、Symfony
モジュールを使用します。
api.suite.yml
にモジュールの設定を行います。
class_name: ApiTester
modules:
enabled:
- REST:
url: http://serviceapp/api/v1/
depends: PhpBrowser
この設定に従ってRESTモジュールはPhpBrowser
に接続するでしょう。Webサービスによっては、XMLまたはJSONレスポンスを扱うことができます。Codeceptionはどちらの形式もうまく扱いますが、もしいずれかが必要でない場合、明示的にモジュールがJSONやXMLを利用するよう指定することができます。
class_name: ApiTester
modules:
enabled:
- REST:
url: http://serviceapp/api/v1/
depends: PhpBrowser
part: Json
APIテストは機能テストとして、そしてSymfony、Laravel5、Zend、または他のフレームワークモジュールを使って実行することができます。そのためには設定ファイルを少し更新する必要があります:
class_name: ApiTester
modules:
enabled:
- REST:
url: /api/v1/
depends: Laravel5
新しいテストスイートを設定できたら、最初のサンプルテストを作りましょう:
$ php codecept generate:cept api CreateUser
これをCreateUserCept.php
と呼ぶこととします。REST APIを介したユーザーの作成をテストするために使用します。
CreateUserCept.php
<?php
$I = new ApiTester($scenario);
$I->wantTo('create a user via API');
$I->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"}');
seeResponseCodeIs
とdontSeeResponseCodeIs
メソッドでレスポンスコードのチェックをする際に、数値の代わりにCodeception\Util\HttpCode
に定義されているHTTPコード定数を使うことができます。
JSONレスポンスのテスト
前のサンプルコードの最終行では、与えられた文字列がレスポンスに含まれることを検証しています。しかしながら、内容の形式によっては同じデータでも異なる結果を受け取ることができるので、この方法に頼るべきではありません。私たちに実際に必要なことは、レスポンスが解析できることを確認することであり、それには期待する何らかの値が含まれています。JSONの場合にはseeResponseContainsJson
メソッドを使うことができます。
<?php
// matches {"result":"ok"}'
$I->seeResponseContainsJson(['result' => 'ok']);
// it can match tree-like structures as well
$I->seeResponseContainsJson([
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
]);
レスポンスに対して、より複雑な検証を行いたい場合があると思います。そのためには ヘルパークラスに独自のメソッドを記述します。最後のJSONレスポンスにアクセスするためには、REST
モジュールのresponse
プロパティーを使用します。次に示すseeResponseIsHtml
メソッドで説明しましょう。
(訳注:「ヘルパー」のリンク先は正しくはこちら)
namespace Helper;
class Api extends \Codeception\Module
{
public function seeResponseIsHtml()
{
$response = $this->getModule('REST')->response;
$this->assertRegExp('~^<!DOCTYPE HTML(.*?)<html>.*?<\/html>~m', $response);
}
}
同じ方法で、リクエストパラメーターや、ヘッダー情報を取得することができます。
JSON構造の検証
APIのテストにおいて、受け取ったデータの検証だけでなく、レスポンスの構造についても検証することはとても一般的です。レスポンスデータは通常一貫したものとしては認識されず、リクエストのたびに変わりますが、JSON/XMLの構造は同じAPIバージョンであれば同じでなければなりません。レスポンスの構造をチェックするための便利なメソッドをRESTモジュールは持っています。
期待するJSONレスポンスを受け取ったら、その構造をJSONPathでチェックすることができます。XPathのように聞こえますが、JSONデータに対して動作するよう設計されています。一方で、JSONをXMLに変換してXPathを使って構造をチェックすることもできます。どちらのアプローチについても正解でかつRESTモジュール内で使用することができます。
<?php
$I = new ApiTester($scenario);
$I->wantTo('validate structure of GitHub api responses');
$I->sendGET('/users');
$I->seeResponseCodeIs(HttpCode::OK); // 200
$I->seeResponseIsJson();
$I->seeResponseJsonMatchesJsonPath('$[0].user.login');
$I->seeResponseJsonMatchesXpath('//user/login');
レスポンス内のフィールドの型を検証する必要がある場合、より詳細な確認が可能です。 JSONレスポンスの構造を定義するseeResponseMatchesJsonTypeアクションを使用して行うことができます。
<?php
$I->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は、容易に学習・拡張できるよう、このシンプルで軽量な定義フォーマットを使用しています。
XMLレスポンスのテスト
REST APIがXML形式で動作する場合についても、似たような方法でデータと構造をテストすることができます。
seeXmlResponseIncludes
メソッドはレスポンスに部分的なXMLをマッチさせる関数で、seeXmlResponseMatchesXpath
は構造を検証するためのものです。
<?php
use Codeception\Util\Xml as XmlUtils;
$I = new ApiTester($scenario);
$I->wantTo('validate structure of GitHub api responses');
$I->sendGET('/users.xml');
$I->seeResponseCodeIs(\Codeception\Util\HttpCode::OK); // 200
$I->seeResponseIsXml();
$I->seeXmlResponseMatchesXpath('//user/login');
$I->seeXmlResponseIncludes(XmlUtils::toXml(
'user' => [
'name' => 'davert',
'email' => 'davert@codeception.com',
'status' => 'inactive'
]
));
XML構造をきれいな方法で構築することのできるXmlUtilsクラスを使っています。toXml
メソッドは文字列もしくは配列をとり、\DOMDocumentインスタンスを返します。もしXMLに属性が含まれていいる関係によりPHPの配列で表現できない場合は、XmlBuilderを使ってXMLを作成することができます。次のセクションでもう少し見てみましょう。
SOAP
SOAP方式のWebサービスは通常、より複雑になります。SOAPサポートを有効にする必要があります。XMLに関する十分な知識も必要とされます。SOAP
モジュールは、WSDLで表されたWebサービスに接続するために、特別な形式のPOSTリクエストを利用します。CodeceptionはPhpBrowser
やいずれかのフレームワーク用モジュールを用いてやり取りを行います。もしフレームワーク用モジュールを選択した場合、SOAPモジュールは自動的に基盤となるフレームワークに接続します。これにより、テスト実行の速度を向上させることができ、詳細なスタックトレースを提供できるようになります。
それではPhpBrowser
とともに使用するSOAP
モジュールを設定しましょう。
class_name: ApiTester
modules:
enabled:
- SOAP:
depends: PhpBrowser
endpoint: http://serviceapp/api/v1/
SOAPリクエストには認証や支払いのようなアプリケーション固有の情報を含みます。この情報はXMLリクエストの<soap:Header>
要素に含まれるSOAPヘッダーによって提供されます。もしこのようなヘッダーを送信したい場合、haveSoapHeader
メソッドを使用することができます。たとえば次のようになります。
<?php
$I->haveSoapHeader('Auth', array('username' => 'Miles', 'password' => '123456'));
このコードは次のXMLヘッダーを生成します。
<soap:Header>
<Auth>
<username>Miles</username>
<password>123456</password>
</Auth>
</soap:Header>
リクエストのボディーを定義するためにはsendSoapRequest
を使用します。
<?php
$I->sendSoapRequest('CreateUser', '<name>Miles Davis</name><email>miles@davis.com</email>');
この呼び出しは次のXMLに変換されます。
<soap:Body>
<ns:CreateUser>
<name>Miles Davis</name>
<email>miles@davis.com</email>
</ns:CreateUser>
</soap:Body>
そして、SOAPモジュールで使用できるアサーションの一覧がこちらです。
<?php
$I->seeSoapResponseEquals('<?xml version="1.0"?><error>500</error>');
$I->seeSoapResponseIncludes('<result>1</result>');
$I->seeSoapResponseContainsStructure('<user><name></name><email></email>');
$I->seeSoapResponseContainsXPath('//result/user/name[@id=1]');
もし長いXMLを記述したくない場合、XmlBuilderクラスの利用を考えてみてください。これはjQueryのようなスタイルで複雑なXMLを構築するのに役に立ちます。
次の例では通常のXMLに替えて、XmlBuilder
を使用しています。
<?php
use \Codeception\Util\Xml;
$I = new ApiTester($scenario);
$I->wantTo('create user');
$I->haveSoapHeader('Session', array('token' => '123456'));
$I->sendSoapRequest('CreateUser', Xml::build()
->user->email->val('miles@davis.com'));
$I->seeSoapResponseIncludes(Xml::build()
->result->val('Ok')
->user->attr('id', 1)
);
XmlBuilder
を使うか、プレーンなXMLを利用するかは、どちらでも構いません。XmlBuilder
も同様にXML文字列を返します。
ヘルパークラスの中でSOAP
モジュールを利用することにより、機能を拡張することができます。\DOMDocument
としてSOAPレスポンスにアクセスするためには、SOAP
モジュールのresponse
プロパティーを使用します。
<?php
namespace Helper;
class Api extends \Codeception\Module {
public function seeResponseIsValidOnSchema($schema)
{
$response = $this->getModule('SOAP')->response;
$this->assertTrue($response->schemaValidate($schema));
}
}
まとめ
CodeceptionはさまざまなWebサービスをテストするために役立つモジュールを2つ備えています。それらを利用するために 新しくapi
スイートを作成する必要がありました。レスポンスボディーだけのテストしかできないわけではないことを覚えておいてください。Db
モジュールを使用することで、CreateUser
の呼び出し後にユーザーが作成されているかどうかテストすることができます。ヘルパーメソッドを利用することでRESTやSOAPを使ったテストシナリオを向上することができます。