WKWebViewを使うアプリのUI自動テストにCalabash-iOSを使ってみた（日本語準備編）


# はじめに

[環境準備編](http://qiita.com/k5n/items/f2ddc1f35f048fe90f3a) の続きです。

Calabash（が利用している Cucumber）は自然言語でテストを記述できるという特徴があります。テストの各ステップを日本語で書けるように準備をします。

bundle exec は省略します。Bundler 使って Calabash をインストールした場合は、

```
cucumber
```

は

```
bundle exec cucumber
```

と置き換えてください。

# 各ファイルの役割と用語解説

calabash-ios gen で生成された雛形の構成は以下のようになっています。

```
features
├── sample.feature
├── steps
│   └── sample_steps.rb
└── support
    ├── 01_launch.rb
    ├── dry_run.rb
    ├── env.rb
    └── patches
        └── cucumber.rb
```

拡張子が feature のファイルにフィーチャ（機能）を記述します。これが一番大きな単位です。１ファイルには１フィーチャしか記述できません。

feature ファイルの書き方は [Cucumber のフィーチャの文法 - Gherkin](http://blog.eiel.info/blog/2013/02/12/gherkin/) が参考になります。

フィーチャの中に幾つかのシナリオを書きます。シナリオは複数あって構いません。正常系とか異常系とか。各シナリオはステップの羅列でできています。

以下は雛形として出力された sample.feature の中身です。

```gherkin
Feature: Sample Feature

Scenario: Sample Scenario
  Given the app has launched
  And I have done a specific thing
  When I do something
  Then something should happen
```

steps の中にはステップに書くコマンドを書いておきます。これを日本語で用意しておけば、ステップを日本語で記述できます。

以下が雛形として出力された sample_steps.rb の中身（コメントは省略）です。これは Ruby で記述されています。

```ruby
Given(/^the app has launched$/) do
  wait_for do
    !query("*").empty?
  end
end

And(/^I have done a specific thing$/) do
  did_something = true

  unless did_something
    fail 'Expected to have done something'
  end
end

When(/^I do something$/) do
end

Then(/^something should happen$/) do
end
```
正規表現を使って、自然言語で書かれたステップ記述を Calabash が用意している API 呼び出しに変換します。ここに日本語でコマンドを用意していけば、日本語でステップを記述できるようになります。

support はいじらないので割愛。

# フィーチャファイルを日本語で書けるようにする

最初から日本語で書けます。以下を実行してみてください。

```
cucumber --i18n ja
```

英語に対応する日本語が表示されます。

```
  | feature          | "フィーチャ", "機能"                                  |
  | background       | "背景"                                           |
  | scenario         | "シナリオ"                                         |
  | scenario_outline | "シナリオアウトライン", "シナリオテンプレート", "テンプレ", "シナリオテンプレ" |
  | examples         | "例", "サンプル"                                    |
  | given            | "* ", "前提"                                     |
  | when             | "* ", "もし"                                     |
  | then             | "* ", "ならば"                                    |
  | and              | "* ", "かつ"                                     |
  | but              | "* ", "しかし", "但し", "ただし"                       |
  | given (code)     | "前提"                                           |
  | when (code)      | "もし"                                           |
  | then (code)      | "ならば"                                          |
  | and (code)       | "かつ"                                           |
  | but (code)       | "しかし", "但し", "ただし"                             |
```

日本語で書くには、feature ファイルの先頭に日本語を指定する行を書くだけです。雛形を日本語化してみましょう。

```
# language: ja
フィーチャ: サンプルフィーチャ

シナリオ: サンプルシナリオ
  前提 the app has launched
  かつ I have done a specific thing
  もし I do something
  ならば something should happen
```

シナリオの各ステップはまだ日本語では書けません。ちなみに「前提」とか「かつ」とか「もし[^when]」とか頭に付いてますが、何を書いても動作は一緒です。以下のように書いても同じ順番でステップが実行されます。

[^when]: 「もし」は条件分岐しそうに思えますが、ifではなくwhenに対応しています。文の先頭にかける単語でwhenに対応する良い日本語が他になかったものと思われます。

```
# language: ja
フィーチャ: サンプルフィーチャ

シナリオ: サンプルシナリオ
  * the app has launched
  * I have done a specific thing
  * I do something
  * something should happen
```

# ステップを日本語で書けるようにする

ここが本命です。英語版は [Calabash 側で汎用的に使えるもの](https://github.com/calabash/calabash-ios/blob/develop/calabash-cucumber/features/step_definitions/calabash_steps.rb) を用意してくれています。プロジェクトの features/steps の下に置くのはプロジェクト専用のものを追加する場所です。

でも日本語版は用意されていません。なので Calabash 側に用意されている汎用のものを参考にして日本語版を作成し、それをプロジェクト用の場所に calabash_japanese_steps.rb  として置くことにします。

各ステップ定義は自然言語で書ける関数と見なせます。以下のように正規表現で記述し、() にマッチした部分を引数として扱います。引数として扱わないものは (?:press|touch) のように ?: を付けます。正規表現の書き方は [Ruby リファレンスマニュアル - 正規表現](https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html) を参考にしてください。

```ruby
Then /^I (?:press|touch) on screen (\d+) from the left and (\d+) from the top$/ do |x, y|
  touch(nil, { offset: { x: x.to_i, y: y.to_i } })
  sleep(STEP_PAUSE)
end
```

ちなみにここでも Given, Then などはどれを選んでも同じです。Given で定義したら Given でしか使えないとか、そういうことはありません。

macro を使ってステップ定義の中で他のステップを呼び出すこともできるので、日本語ステップ定義から英語ステップを呼び出すという方法が取れます。こんな感じですね。

```ruby
Then /^画面の\((\d+), *(\d+)\)座標を(?:タッチ|タップ)する$/ do |x, y|
  macro "I touch on screen #{x} from the left and #{y} from the top"
end
```

Ruby 知らない人のために説明しておくと、ダブルクォーテーション文字列の中には #{x} のようにして変数の値を埋め込めます。またパーセント記法というのを使うと、ダブルクォーテーション自体を含む文字列でダブルクォーテーションのエスケーブが必要なくなります。

```ruby
Then /^"([^\"]*)"を(?:押す|タッチする|タップする)$/ do |name|
  # % の後の文字は自由に選べるので、文字列が含まない文字にする。
  macro %(I touch "#{name}")
end
```

しかしここでは英語版を呼び出すことはせず、以下のように定義の中身を英語版と同じにしました（その方が楽）[^englishsteps]。

```ruby
Then /^画面の\((\d+), *(\d+)\)座標を(?:タッチ|タップ)する$/ do |x, y|
  touch(nil, { offset: { x: x.to_i, y: y.to_i } })
  sleep(STEP_PAUSE)
end

Then /^"([^\"]*)"を(?:押す|タッチする|タップする)$/ do |name|
  touch("view marked: '#{name}'")
  sleep(STEP_PAUSE)
end
```

日本語化したものは長いので [Gist](https://gist.github.com/k5n/a08b2855d828dc2723160e2d06244a4d) に置いておきました。動作確認できていないものが沢山なので、多分（特に正規表現部分に）バグが一杯あります。

[^englishsteps]: CalabashのバージョンアップでAPIが変わる可能性を考えると、英語版を呼び出すアプローチの方が良いかもしれません。

# まとめ

Calabash のテストを日本語で書けるようにしました。後はバリバリテストを書いて・・・といきたいところですが、まだ

 - 本当に WKWebView を扱えるのか？
 - 日本語の文字入力は大丈夫か？
 - ビュー階層を見るにはどうするのか？
 - ビュー階層の中から要素を指定するにはどうするのか？
 - テストシナリオ実行前にアプリを再インストールすることは可能か？

などなど確認したいことがあります。[次の記事](http://qiita.com/k5n/items/ec546e08e3f960ecb106)ではそれらについて色々試してみます。