うさぎ組

kyon_mm with software

このエントリーをはてなブックマークに追加

現在時刻のテストをする 2/3 Cucumber-groovyでfeatureを書く #gadvent

はじめに

3回にかけてGroovyによるATDDを紹介します。お題は時刻によって挨拶文が変わるというものです。この連載?はG* Advent Calendar2013の投稿になっております。 また内容自体は本来はシステムテスト自動化カンファレンスでDEMOする内容でした。プロダクトコードをF# でつくったはいいものの、説明だったり動かす環境だったり、スクリプトのローカル依存具合がひどくってまだ公開できる段階にありません。。。(すいません)連載が終わる頃にはGroovy版で作りなおすか、F#の環境依存を解決するかで公開したいです。

http://qiita.com/advent-calendar/2013/gastah/participants

Acceptance Testをどうやって書いていくか

Cucumberにしろ何を使うにしろ、受入れテストを書く場合はそれが顧客がわかる言葉でなければいけません。 可能な限り顧客に協力して設計、実装するのがよいですがそうもいかない場合もあるでしょう。

Cucumberで書く場合には次のステップが考えられます。

  1. feature - ある程度のシナリオで分割する。
  2. feature - シナリオの概要を記述する。
  3. feature - 具体的な手順を記述する。
  4. feature - パラメータを記述する。
  5. test - 各ステップを実装する。
  6. テストを実行する。

featureファイルを作成する

シナリオ名.featureでファイルをresources配下に作成します。日本語のキーワードを使いたい場合は先頭に「# language: ja」を追記します。

行コメントは# 始まりになります。

シナリオの概要を記述する

機能: という箇所にはシナリオを書きます。日本語で書くことができます。基本的にここに書いたものはテストコードに影響をあたえません。役割的にはクラスヘッダコメントのようなものです。外部設計書という存在も近いと思います。

シナリオ間の重複を気にするよりは目的、範囲、流れを気にするほうがよいと思います。目安は1行から5行程度。

# language: ja

機能: トップページに挨拶を表示する。挨拶の内容は時間帯によって変わる。
  システムが正常に動作しているときのトップページには常に表示しておく。
  デフォルトでは日本語の挨拶を表示するが、各地域の言語にも対応する。
  対応する言語については以下で示す。

シナリオの概要を記述する

基本的にはパラメタライズでいいと思うので「シナリオテンプレート」で書きます。これはxUnitで言うテストメソッド名のようなものです。

自分の場合は「どんな機能,何に着目している」を必ず書くようにしていて、これでスッキリと書けることが多いです。

シナリオテンプレートに続いて手順を「前提,もし,かつ,ならば」などをつかって書きます。Spockユーザーにとっては驚きかもしれませんが、これらの順番に制限はありません。ここで使う記号はエスケープしなければいけない場合が多いです。IntelliJ が黄色かったり、文字色が途中から黒になっている部分があればそれはエスケープすべき文字が途中にあること示しています。

最初はパラメタライズできる項目が思いつかないと思うので<>をつかった変数埋め込み部分はつくらなくてよいです。

パラメータを記述する

パラメータになりそうなものは何かを手順から探します。気をつけるのは「まだ手順に表現されていないものが実は存在していて、そこにパラメータがある」という可能性が高いということ。

パラメータが何かを想像しにくい場合はユースケース記述にするとよい場合があります。つまり「<誰>が<何>に|を<どうする>」と必ず書くことです。誰にはユーザー、システム両方が入ります。

パラメータは「例:」というキーワードの次行から続けて書きます。最初の行が因子のならびでヘッダーとしての役割です。次の行からが実際にテストケースとなる値です。このヘッダーの文字列を<>で囲ったものを各手順の<因子名>で使えるようになります。

シナリオテンプレート:挨拶を表示するときに時間帯に着目する
  前提 <ある時間帯> の適当な時刻にアクセスする
  もし トップページを開いたなら
  ならば <特定の挨拶文> と挨拶を返す
例:
  | ある時間帯 | 特定の挨拶文 |
  | 朝     | おはよう   |
  | 昼     | こんにちは  |
  | 夜     | こんばんは  |

最初はこのように朝、昼、夜といった曖昧なパラメタライズになるのですが、ここからどんどん詳細にしていきます。

「朝、昼、夜の境界はなにか?」「その時刻を判断するのはどのタイムゾーンか?」「誰にとっての時刻を採用するのか?」「挨拶は日本語のみか?」

どこまでも詳細にできますが、重要なのは「機能:」で書いた範囲に収まった手順とパラメタライズになっているかです。より広範なパターンが必要になった場合は、別のシナリオに書きおこしましょう。

シナリオテンプレート:挨拶を表示するときにクライアントの時刻に着目する
  前提 サーバーの時刻が <サーバー時刻> で クライアントの時刻が <クライアント時刻> ちょっきりにアクセスする
  もし トップページを開いたなら
  ならば <特定の挨拶文> と挨拶を返す
例:
  | サーバー時刻   | クライアント時刻 | 特定の挨拶文 |
  | 05:59:59       | 05:59:59 | こんばんは  |
  | 06:00:00       | 05:59:59 | こんばんは  |
  | 05:59:59       | 06:00:00 | おはよう   |
  | 06:00:00       | 06:00:00 | おはよう   |
  | 11:59:59       | 11:59:59 | おはよう   |
  | 12:00:00       | 11:59:59 | おはよう   |
  | 11:59:59       | 12:00:00 | こんにちは  |
  | 12:00:00       | 12:00:00 | こんにちは  |
  | 17:59:59       | 17:59:59 | こんにちは  |
  | 18:00:00       | 17:59:59 | こんにちは  |
  | 17:59:59       | 18:00:00 | こんばんは  |
  | 18:00:00       | 18:00:00 | こんばんは  |

testを記述する

featureの各ステップに対応するGroovyのテストコードを書く必要があります。実際にはこの「特定のステップ」と「Groovyのテストメソッド」は正規表現によってマッチしたものが実行されます。普通のGroovyスクリプトになります。

IntelliJを使っている場合だと、featureファイルの各ステップにカーソルを合わせてAlt + Enterを押すとそのステップに該当するようなスケルトンメソッドを任意のファイルに生成できます。(新規作成だったり、既存のファイルを選べたり。)

ファイルの先頭(importのあと)でHooksと言語(ジャーコン)をthisにmixinします。

各ステップの正規表現でグループマッチをしておくと後方参照としてメソッドの引数に渡されます。パラメタライズの一行ごとに対応するテストメソッドを書くこともできますし、<>で入ってくる部分を(.+)のようにして引数として受け取ることもできます。

package feature

import cucumber.api.groovy.Hooks //フィーチャーファイルとの接合をやってくれる
import cucumber.api.groovy.JA //どの言語で書きたいか

this.metaClass.mixin(Hooks) // このスクリプトファイルにmixinする
this.metaClass.mixin(JA)    // このスクリプトファイルにmixinする

            // featureファイルの該当行と正規表現マッチして適合する定義を実行する。
// 基本的には普通のGroovyとして書ける

前提(~'^(.+) の適当な時刻にアクセスする$') {
    println it
}
前提(~'^(.+)\\:(.+)\\:(.+) ちょっきりにアクセスする$') {h,m,s ->
    println "$h:$m:$s"
}
前提(~'^サーバーの時刻が (.+) で クライアントの時刻が (.+) ちょっきりにアクセスする'){h,m ->
    println "$h, $m"
}
もし(~'^トップページを開いたなら$') {->
    println "top page"
}
ならば(~'^(.+)と挨拶を返す$') {
    assert true
}
前提(~'^クライアントの時刻が (.+) (.+) (.+)ちょっきりにアクセスする$') {h,m,s ->
    println "$h:$m:$s"

}

これの何が最悪かって、パラメタライズによって生成されるテストケース名とマッチするので、featureの手順を気をつけていても、パラメータの書き方によっては予想しないところで別のテストメソッドに適合する場合がありまして。。。

っていうのが前回のパラメタライズの最後のパターンです。こういった具合にCucumberを使うと正規表現スキルが試されるのと、featureファイルの書き方にかなりの注意を払う必要があります。。。 Cucumberを使うのは最低限に抑えたいですね。

で、AHCでのテスト実装は次回に

手順を書いていたら長大になってしまったので、続きは次回にします。次回でSpockの場合も書きたいけど、分量が心配です。。。