Laravel5.1のテストでphp-webdriverやGuzzleを使う


Laravel5.1から、新テスト記述法が導入され、それはそれで素晴らしいのですが、細かいテストを書こうとするとちょっと限界を感じたので、php-webdriverやGuzzleを組み合わせる方法をメモしておきます。

なお、ここでは、php-webdriverやGuzzleをLaravelで使う導入のメモだけなので、php-webdriverやGuzzleの使い方については、下記を参考にしてください。

* [php-webdriverの使い方](http://qiita.com/zaburo/items/f11357170953a3c34b8f)
* [Guzzleの使い方](http://qiita.com/zaburo/items/13bfdc349e1421421c98)


##5.1のテスト(旧laracast/integrated)の難点

5.1でのテストはちょっとテストを書くにはいいのですが、下記のような問題があります（私の調査不足だけかもしれませんが）。

* JavaScriptを多用したUIのテストは厳しい。
* POST/GETとかでテストだけの記述を覚えないといけない。
* visitで($baseUrlを変更しても）外のサイトをテストできない。

なので、

* UIのテストは中途半端なSelenium使うなら、php-webdriver使った方が手間がない。
* Guzzleを利用すれば、テストだけでなく、普通の開発時もノウハウが共通化できる。

と感じたのでメモするというわけです。

とは言え、Laravel以外で使う場合となんら変わったことはありません。

##composer.jsonの設定

require-devにguzzleとwebdriverの記述（下の2行）を追加し、

```php
    "require-dev": {
        "fzaninotto/faker": "~1.4",
        "mockery/mockery": "0.9.*",
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1",
        "guzzlehttp/guzzle": "^6.1",
        "facebook/webdriver": "^1.0"
    },
```
composer updateを実行します。

```bash
composer update
```

##テストを書く

Lravelではtests以下にテストを書きます。既に存在しているサンプルのExampleTest.phpを編集すればいいでしょう。

既にテスト対象のサイトやAPIはあるものとして、下記のようなコードを書きます。
あくまで雰囲気だけですが、下記のようなことを行っています。

* UIではChromeを起動して閉じている。
* APIのリクエストはGuzzleで行ない、標準のassertで評価している。
* QueryBuilder（もちろんEloquentでも！）でDBの内容を参照し、assertしている。

標準のテストより記述は少々複雑ですが、制限を気にしる必要がないので気は楽かなと。

```php
<?php

use Illuminate\Foundation\Testing\WithoutMiddleware;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;

use Facebook\WebDriver;
use Facebook\WebDriver\WebDriverExpectedCondition;
use Facebook\WebDriver\WebDriverBy;
use Facebook\WebDriver\Remote;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;

class ExampleTest extends TestCase
{
	//UIのテスト
    public function testWebUI()
    {
        //準備（java -jar java -jar selenium-server-standalone-2.48.2.jar済である必要あり）
        $host = 'http://localhost:4444/wd/hub';
        //chromedriverを指定（chromedriverをダウンロードして/user/local/bin等に入れておく。
        $driver = RemoteWebDriver::create($host, DesiredCapabilities::chrome());

        // //テストするサイトに移動
        $driver->get('http://localhost/test/index.php');

        //いろいろな処理（省略）

        //閉じる
        $driver->close();
    }

	//APIのテスト
	public function testWebAPI_GET()
    {
        //
        $client = new GuzzleHttp\Client();

        //get
        $res = $client->get('http://localhost/test/api.php',[
            'query'=>[
                'name'=>'POO',
                'email'=>'poo@poo.com',
            ],
        ]);

        //取得 第2引数にtrueつけると連想配列
        $obj = json_decode($res->getBody());

        //取得
        $status = $obj->status;
        $message = $obj->message;
        $email = $obj->email;
        $rows = $obj->data;

        //loop（参考）
        foreach ($rows as $row) {
            //echo $row->name;
        }

        //評価
        $this->assertEquals("OK",$status);
        $this->assertEquals("Hello POO.",$message);
        $this->assertRegExp('/^Hello.+\.$/',$message);
        $this->assertCount(2,$rows);

        //データベースと連携
        $user = \DB::table('users')->where('name','POO')->first();
        $this->assertEquals("poo@poo.com",$user->email);
    }

}
```

##テストの実行

###実行の前に

php-webdriverを正常に処理するためにはSelenium Server(Standalone)が起動している必要があります。
必要に応じてダウンロードし起動させておきます。
詳しくは[こちら](http://qiita.com/zaburo/items/f11357170953a3c34b8f)をご覧下さい。

###実行

Laravelにおけるテストの実行はtestsディレクトリにいるもとすると、

```bash
./vendor/bin/phpunit tests/
```
とすれば実行されます。

##その他

LaravelではTest用クラスが拡張されているため、独自のassertが使えます。
詳しくは[こちら](http://laravel.com/docs/5.1/testing)。