Drupal 8 に Drupal 向け Behat のスタンドアロンでの導入とシンプルなテストシナリオの作成

Drupal 8 では BDD でテストするためのフレームワークとして Behat を使用する事が出来ます。
この記事では、Drupal 向け Behat の導入とテストスクリプトの作成を解説します。

## Drupal 向け Behat のスタンドアロンインストール

Drupal 向け Behat のインストールは以下の手順で行います

1. Behat 用のディレクトリの作成
2. Composer を利用して、必要パッケージの導入
3. Behat の設定

### Behat 用のディレクトリの作成
適当なディレクトリに Behat のテストを行うディレクトリを作成します。

```bash
cd /path/to/behat/
mkdir behat-tests
cd behat-tests
```

### Composer を利用して、必要パッケージの導入

Composer で必要なモジュールを導入します。Composer のインストールが済んでいない場合は、[Composer 公式サイトのコマンド](https://getcomposer.org/download/)を実行して導入しましょう。また、日本国内の回線を使用して Composer でパッケージの導入をすると、いわゆる[『光遅い問題』](https://speakerdeck.com/hirak/composer-keynote)にぶつかるので、事前に対処しておく事をオススメします。
必要なモジュールを記述するため、以下のコマンドで composer.json を作成します。

```bash
touch composer.json
```

composer.json を作成したら、[Drupal 向け Behat のインストールマニュアル](https://behat-drupal-extension.readthedocs.io/en/3.1/localinstall.html)にある通りパッケージを記述します。

```json:composer.json
{
  "require": {
    "drupal/drupal-extension": "~3.0",
    "guzzlehttp/guzzle" : "^6.0@dev"
},
  "config": {
    "bin-dir": "bin/"
  }
}
```

終わったら、パッケージをインストールします。

```bash
composer install
```

### Behat の設定

Behat の設定を記述するため、behat.yml を作成します。

```bash
touch behat.yml
```

behat.yml を作成したら、[公式サイトのドキュメント](https://behat-drupal-extension.readthedocs.io/en/3.1/localinstall.html)を参考に、Drupal のサーバのアドレス（例：http://drupal-server-addr.example.com/ ）を以下のように設定します。

```yaml:behat.yml
default:
  suites:
    default:
      contexts:
        - FeatureContext
        - Drupal\DrupalExtension\Context\DrupalContext
        - Drupal\DrupalExtension\Context\MinkContext
        - Drupal\DrupalExtension\Context\MessageContext
        - Drupal\DrupalExtension\Context\DrushContext
  extensions:
    Behat\MinkExtension:
      goutte: ~
      selenium2: ~
      base_url: http://drupal-server-addr.example.com/ #ウェブサーバのアドレス
    Drupal\DrupalExtension:
      blackbox: ~
```

記述が終わったら、以下のコマンドで Behat の初期化を行います。

```bash
./bin/behat --init
```

Behat 用ディレクトリの下に `/path/to/behat/behat-tests/features/bootstrap/FeatureContext.php` が作成されていたら成功です。
最後に、定義済みのステップ一覧を以下のコマンドで表示します。

```bash
./bin/behat -dl --lang en
default | Given I am an anonymous user
default | Given I am not logged in
default | Given I am logged in as a user with the :role role(s)
default | Given I am logged in as a/an :role
default | Given I am logged in as a user with the :role role(s) and I have the following fields:
default | Given I am logged in as :name
（省略）
```

`--lang en` で英語に指定しておかないと、`Given` や `When`、`Then` が日本語化されて分かりづらくなるのと、他の Behat のテストスクリプトが読みづらくなるので、英語指定で確認するようにすることを推奨します。

## Behat テストスクリプトの作成

インストールが成功したら、簡単なテストスクリプトを作成してみます。


実際のテストスクリプトを書く前に、Behat のテストスクリプトの流れについて説明します。
Behat でのテストスクリプトの例は下記のようになります。

```gherkin:example.feature
# Feature 名
Feature: Feature Name
  # Scenario 名
  Scenario: Scenario Name
    # シナリオの中身
    Given hogehoge
    When foobar
    Then xyzzy
```

シナリオの流れは下記の通りになります。

1. Given で前提条件を記述する
  - 例： Given I am an anonymous user
2. When でこれから行う動作を記述する
  - 例： I go to "admin/content" 
3. Then で期待される結果を記述する
  - 例： Then the response status code should be 403

これらの記述は [gherkin という DSL](http://qiita.com/shmurakami/items/f10b3aff5b4c9672eaca)で書かれているので、gherkin 対応済みエディタや gherkin に対応できるようなプラグインを導入しておくと作業が楽になると思います。

本題に戻り、実際にテストシナリオを作成します。
テストスクリプトの作成場所は、`/path/to/behat/behat-tests/features` になります。ここに、`*.feature` という拡張子のファイルを作成します。
今回のテストシナリオでは、`test.feature` というファイル名にします。

```bash
touch features/test.feature
```

作成するテストシナリオは下記になります。

<dl>
<dt>
Feature 名
</dt>
<dd>
テストフィーチャー
</dd>
<dt>
シナリオ名
</dt>
<dd>
未ログインで存在しないページを表示
</dd>
<dt>
前提条件
</dt>
<dd>
ユーザはログインしていない
</dd>
<dt>
行う動作
</dt>
<dd>
foobar にアクセス
</dd>
<dt>
期待する結果
</dt>
<dd>
HTTP ステータスコードが 404 になる
</dd>
</dl>

このシナリオに合致する、ステップを `./bin/behat -dl --lang en` から探し出します。

### 前提条件：ユーザはログインしていない

ユーザをログインしていないことを示すステップは、以下が挙げられます。

```gherkin
Given I am an anonymous user
Given I am not logged in
```

ステップにはこのように言い回し以外は違いがなかったり、逆に似ていても受け付けるパラメータが異なっていたりすることがあります。何度か試してみて、シナリオに合うステップを探し出します。

### 行う動作：foobar にアクセス

特定 URL にアクセスするステップは、以下が挙げられます。

```gherkin
When /^(?:|I )go to "(?P<page>[^"]+)"$/
```

When に続く部分を見ると、正規表現になっていることが分かります。ステップを記述する際は、この書式に合わせる必要があるため、以下のどちらかの記述でなければなりません。（正規表現の ":?" については [PCRE 正規表現構文のサブパターンのページ](http://php.net/manual/ja/regexp.reference.subpatterns.php)を確認してください）

```gherkin
When go to "foobar"
When I go to "foobar"
```

### 期待する結果：HTTP ステータスコードが 404 になる

HTTP ステータスコードが 404 であることを期待するステップは、以下が挙げられます。

```gherkin
Then I should get a :code HTTP response
```

行う動作の時とは異なり正規表現は存在しませんが、`:code` に期待する結果を代入した形でステップを記述する必要があるため、下記のように書きます。

```gherkin
Then I should get a 404 HTTP response
```

ここまでの内容を元に、`test.feature` を記述すると下記のようになると思います。

```gherkin:test.feature
# Feature 名
Feature: テストフィーチャー
  # Scenario 名
  Scenario: 未ログインで存在しないページを表示
    # シナリオの中身
    Given I am not logged in
    # Given I am an anonymous user も可
    When I go to "foobar"
    Then I should get a 404 HTTP response
```

テストを実行する時は、下記の `./bin/behat` を実行します。

```bash
./bin/behat
Feature: テストフィーチャー

  Scenario: 未ログインで存在しないページを表示             # features/te
st.feature:4
    Given I am not logged in              # Drupal\DrupalExtension\Conte
xt\DrupalContext::assertAnonymousUser()
    When I go to "foobar"                 # Drupal\DrupalExtension\Conte
xt\MinkContext::visit()
    Then I should get a 404 HTTP response # Drupal\DrupalExtension\Conte
xt\MinkContext::assertHttpResponse()

1 scenario (1 passed)
3 steps (3 passed)
0m0.77s (12.85Mb)
```

もし、スペルミスなどで定義されていないステップを書いてしまった場合は、下記のようにエラーメッセージで定義の追加の仕方が表示されます。

```bash
./bin/behat
Feature: テストフィーチャー

  Scenario: 未ログインで存在しないページを表示                  # featur
es/test.feature:4
    Given I am not logged in                   # Drupal\DrupalExtension\
Context\DrupalContext::assertAnonymousUser()
    When I go to "foobar"                      # Drupal\DrupalExtension\
Context\MinkContext::visit()
    Then Then I should get a 404 HTTP response

1 scenario (1 undefined)
3 steps (2 passed, 1 undefined)
0m0.73s (12.76Mb)

--- FeatureContext has missing steps. Define them with these snippets:

    /**
     * @Then Then I should get a :arg1 HTTP response
     */
    public function thenIShouldGetAHttpResponse($arg1)
    {
        throw new PendingException();
    }
```

もし、定義にないステップを作成したいなどの拡張を行う場合はこれを参考に追加していくと良いと思います。

## まとめ

* Drupal 向けの Behat のスタンドアロンでの導入の手順を示した
  * インストールは Composer を使用するので、日本国内のサーバは『光遅い問題』に対応した方が良い
* 簡単なテストスクリプトの作成を行い、Drupal での BDD テストの第一歩を踏み出せるようになった
  * 定義されていないステップはエラーメッセージが出るとともに拡張の方法が示される


---
### Behat on Drupal 8 の記事一覧

| 回 | タイトル |
|:-:|:-:|
|  第1回 |  **Drupal 8 に Drupal 向け Behat のスタンドアロンでの導入とシンプルなテストシナリオ** |
|  第2回 |  [Drupal 向け Behat の拡張その1 複数ステップの実行とカスタムステップの作成（前編）](http://qiita.com/tom_konda/items/01ec5040d9cc3c04c5a6) |
| 第3回 |[Drupal 向け Behat の拡張その2 カスタムステップの作成（後編）](http://qiita.com/tom_konda/items/d54d4b66cdd4c310703e)|