Behatを触ってみたメモ


# 概要
備忘録的なものです。
PHP製のサービスに対してBDD, E2Eテスト周りに手を出しておこうと思っていろいろ調べた結果、Behatが良さそうだったので触ってみました。

# 構成
各要素のバージョンは
- PHP: 5.6.17
- behat/behat: v3.1.0
- behat/mink: v1.7.1
- behat/mink-extension: v2.2
- behat/mink-selenium2-driver: v1.3.1
- phantomjs: 2.1.7

となってます。
現段階(2016/07/11)では behat/behat v3.1.0 がlatestのようですが、http://docs.behat.org がオフィシャルだと思いますがv3.0にリンクされてますね。と言うかlatestとstableがそれぞれいくつなのかサイト上で分かりづらい…


# 導入
インストールはcomposerを使いましょう。

```json:composer.json
"require-dev": {
  "behat/behat": "^3.1",
  "behat/mink": "^1.7",
  "phpunit/phpunit": ">=4.1 < 5",
  "behat/mink-extension": "^2.2",
  "behat/mink-selenium2-driver": "^1.3",
},
```

こんな感じになってれば良いでしょう。behat自体にはassertion機能が無いらしいので、phpunitを入れてます。

phantomjsはnpmで入れれば良いと思います。

```json:package.json
"devDependencies": {
  "phantomjs": "^2.1.7"
}
```

こんな感じですかね。

# 要素
behatって何？というところからもろもろ触って、なんとなくこんな感じなのだろう、というのをまとめていきます。
なお、使い方についてはあまり書きません。

各libraryの役割をざっと記述すると

- behat/behat : Behat本体
- behat/mink : サイトにアクセスするための Session, Assertion, Element などなどのベース機能
- behat/mink-extension : Minkを使うためのContextを提供
- behat/mink-selenium2-driver : Minkが提供するDriverInterfaceに則ったSelenium向けのDriver. 要はSeleniumへのつなぎ込みを管理してくれるもの

## behat/behat
Behatの本体ですね。当然ですがBehatの基本的な機能はこの中にあります。
Behatの初期化コマンドを実行するとfeaturesディレクトリと、FeatureContextクラスが作られます。

このContextクラスがTestSuiteにあたります。

### features
Contextを呼び出すために .featureファイルを作ります。featureファイルはgherkinというDSLで書かれています。
こんな感じ

```gherkin:sample.feature
Feature: Listing command
  In order to change the structure of the folder I am currently in
  As a UNIX user
  I need to be able see the currently available files and folders there

  Scenario: Listing two files in a directory
    Given I am in a directory "test"
    And I have a file named "foo"
    And I have a file named "bar"
    When I run "ls"
    Then I should get:
      """
      bar
      foo
      """
```

このようなファイルをbehatの引数に渡すと実行してくれます。
じゃあこのファイルがどうやってContextクラスと関連付けられるのか？というと、behat.ymlという設定ファイルを使います(デフォルトの挙動については言及しません)。

### behat.yml
behat.ymlについてはドキュメントにも記述が少なくて正確に理解できてません。

behat.ymlはパスによっては自動で読み込まれますが、おとなしく -c オプションでパスを指定した方が良いでしょう。
中身はこんな感じです。

```yml:behat.yml
default:
  suites:
    default:
      contexts:
        - FeatureContext
        - FeatureWithMinkContext
  extensions:
    Behat\MinkExtension:
      base_url: http://localhost/behat
      selenium2: ~
```

詳細については分かりませんので、ちゃんと把握したら編集します。contextsに記述したContextクラスが読み込まれるので、対象にしたいファイルはここに記述します。
extensionsは後述します。


## behat/mink
Webサイトにアクセスして中身を扱うための機能を提供していますが、Mink-Extension, driverにより隠蔽されてるのであまり実体を気にすることは少ないんじゃないかなと思います。


## behat/mink-extension

これを継承したContextを作るとWebサイトにアクセスして中身の検証を行うことが出来ます。
mink-extensionはいくつかのDriverに対応していて、使いたいDriverをbehat.ymlに記述すれば読み込まれるんだろう、と思ってます。対応しているDriverは `vendor/behat/mink-extension/src/Behat/MinkExtension/ServiceContainer/Driver` 以下のファイルを見れば良いでしょう。自前でDriverが作りたかったらDriverFactoryを実装すれば良いでしょうね。

ということで、Mink-ExtensionはDriverを呼び出してWebサイトにアクセスさせやすくする機能、Minkを呼び出してAssertion出来るようにするというものです。Adaptor的な感じですかね。MinkとかDriver周りは隠蔽してこれだけ気にすればContextは書けるようにしてるんじゃないかと思います。


## behat/mink-selenium2-driver
Mink-Extensionから呼ばれる、実際にWebサイトにアクセスするためのDriverです。今回はPhantomJSを使いたいのでこれを使っています。経緯は知りませんが、selenium2-driverはphantomjsに対応しています。
別でbehat/mink-selenium-driverというのもいますが、selenium2の方がintegrateしやすいのでしょう。ソースを眺めた感じ、TravisとかJenkinsとも連携できるようです。多分。。

`vendor/behat/mink-selenium2-driver/bin` 以下にphantomjs, seleniumの起動スクリプトが格納されていますが、これらが vendor/bin にリンクされてないのはなんでだ？

### phantomjs

起動スクリプト内でphantomjsを
`phantomjs --webdriver=4444` として起動しています。Webサイトにアクセスする場合、アクセスする前にDriverのSessionを作るため、起動していなと以下のようなエラーになります。これはphantomjsを起動すれば解消します。

>  Could not open connection: Curl error thrown for http POST to http://localhost:4444/wd/hub/session with params: {"desiredCapabilities":{"tags":["shmurakami.local","PHP 5.6.17"],"browser":"firefox","ignoreZoomSetting":false,"name":"Behat feature suite","browserName":"firefox"}}


さて、この状態でContext内で

```php:Context
class AnyContext extends MinkContext
{
  /**
   * @Given access to index page
   */
  public function Webサイトにアクセス()
  {
      $this->visit('index.php');
  }

  /**
   * @Then show page title
   */
  public function ページタイトルが表示されている()
  {
      $this->assertPageMatchesText("/Behat Test/");
  }
}

```

こんな感じのContextと

```feature:example.feature
# language: en
Feature: Test of MinkExtension
  In order to run behat with Mink
  As a sample project

  Scenario: index.php
    Given access to index page
    Then show page title
```

こんなfeatureを書いてbehatを実行すれば、実際にindex.phpにアクセスしてテキストが表示されているかをテストできます。



# まとめ
どういう要素がどう動いているかをまとめてみました。間違ってるかもしれませんので、その場合はコメントでご指摘ください。
behat.ymlとかはまだ謎が多いので、これから実際に使っていって何かあれば追記していこうと思います。