2016年7月25日月曜日

SpecFlowフィーチャの記載サンプル一通り

SpecFlow上で実行できる、フィーチャの記載サンプルをいくつか以下に示す。
生成されるStep Definition についても、コード例を同時に示す。

主に、以下を参考に記載:

最低限のSpecFlow準備

もし、記載したフィーチャを実際に動作させて試したい場合には、単体テストプロジェクトを新規追加して、この投稿の「SpecFlow初期化」部分だけを実行すれば簡単です。

上記に加えて、App.config の設定を、以下のとおりに変更すると日本語でのフィーチャの記載がさらに楽になります。

<specFlow>
   <language feature="ja-JP" />
   <unitTestProvider name="MsTest" />
</specFlow>

上記赤字部分の設定で、フィーチャファイルがデフォルトで日本語状態になります。

記載サンプル

普通のシナリオ


ごくごく普通のシナリオの記載。

機能: 計算機機能
   計算機。電卓風計算機能の動作仕様。

シナリオ: 二つの値を足し算する
   前提 計算機に 50 と 100 を入力した
   もし プラスボタンを押した
   ならば 結果として 150 が表示される

各部分の説明

「機能:」に記載された部分から、「シナリオ:」まで直前の行までの記載は、テスト実行時には無視される。
「シナリオ:」部分に、確認したいシナリオのタイトルを記載する。これは、一つのフィーチャの中では、重複してはならない(同じシナリオは、一つのフィーチャに二つは定義できない)
「前提」、「もし」、「ならば」に該当する部分の記載が、テスト実行時のインプットとなる。

コード例:


[Given(@"計算機に (.*) と (.*) を入力した")]
public void 前提計算機にとを入力した(int p0, int p1)
{
// 実装は省略
}

[When(@"プラスボタンを押した")]
public void もしプラスボタンを押した()
{
// 実装は省略
}

[Then(@"結果として (.*) が表示される")]
public void ならば結果としてが表示される(int p0)
{
// 実装は省略
}

上記のコード例でシナリオのテストを実行した場合、メソッド「前提計算機にとを入力した(int p0, int p1)」の引数p0には”50”が、p1には、”150”が、それぞれ引き渡される。


テーブル

複数の値を、一度に「前提」「もし」「ならば」への入力して用いたい場合の記載例。

記載例:


機能: 会計年度ごと集計
   会計年度ごとに集計処理を行う。

シナリオ: 年度ごと集計
   前提 各年月の金額が以下の通りの内容である場合
   | 年月     | 金額   |
   | 201312 | 1100 |
   | 201401 | 1200 |
   | 201403 | 1300 |
   | 201404 | 1401 |
   | 201405 | 1502 |
   もし 会計年度ごと集計機能を起動した
   ならば 各年度の集計金額は以下の通りとなる
   | 年度   | 合計金額 |
   | 2013 | 3600 |
   | 2014 | 2903 |

上記の「前提」、「ならば」の下に記載した”|”で挟まれた領域が、テーブルとして、Bindingのコードへ引き渡される。

コード例:

[Given(@"各年月の金額が以下の通りの内容である場合")]
public void 前提各年月の金額が以下の通りの内容である場合(Table table)
{
    // 実装は省略
}

[When(@"会計年度ごと集計機能を起動した")]
public void もし会計年度ごと集計機能を起動した()
{
    // 実装は省略
}

[Then(@"各年度の集計金額は以下の通りとなる")]
public void ならば各年度の集計金額は以下の通りとなる(Table table)
{
    // 実装は省略
}

上記コード例での引数「Table table」の中身は、以下のように取り出す。

string val = table.Rows[0][0]; // 1行目の1列目の値をとりだす
string val = table.Rows[0]["合計金額"]; // 1行目の「合計金額」列の値をとりだす

// テーブルの各行の値を取り出す
foreach (TableRow row in table.Rows)
{
    string year = row["年度"]; // 「年度」列の値をとりだす
    string amount = row["合計金額"]; // 「合計金額」列の値をとりだす
}

なお、中身は、記載内容にかかわらず、文字列として扱われる。
(ただの数値が記載してあっても、文字列)



ドキュメント文字列(Doc Strings)


あるステップに対して、複数行の文字列データを引数として引き渡す記載方法。

記載例:

機能: 質問投稿機能
   質問を投稿する機能。

シナリオ: 質問を投稿
   前提 質問者名に名前を入力していない
     かつ 以下質問を入力している
"""
購入した商品の産地について、
届けられた商品に記載された産地が、
サイト上の商品説明と一致してないように見える。
この商品の産地について、正しい情報を教えて欲しい。
"""
   もし 質問を投稿した
   ならば 結果として新規の投稿受付番号が表示される
     かつ オペレータに投稿受付番号が通知される


二つ目の前提「かつ」部分の下で「”””」で囲まれた部分が、試験実行時に最後の引数として引き渡される。

コード例:

[Binding]
public class 質問投稿機能Steps
{
   [Given(@"質問者名に名前を入力していない")]
   public void 前提質問者名に名前を入力していない()
   {
       // 実装内容は省略
   }
   
   [Given(@"以下質問を入力している")]
   public void 前提以下質問を入力している(string multilineText)
   {
       // 実装内容は省略
   }
   
   [When(@"質問を投稿した")]
   public void もし質問を投稿した()
   {
       // 実装内容は省略
   }
   
   [Then(@"結果として新規の投稿受付番号が表示される")]
   public void ならば結果として新規の投稿受付番号が表示される()
   {
       // 実装内容は省略
   }
   
   [Then(@"オペレータに投稿受付番号が通知される")]
   public void ならばオペレータに投稿受付番号が通知される()
   {
       // 実装内容は省略
   }
}
なお、この状態で実行した場合には、”multilineText”引数には、以下のような内容が引き渡される。


シナリオアウトライン

同じ内容のシナリオに対して、複数パターンの値を記載する必要がある場合の記載方法。

記載例:

シナリオアウトライン: 二つの値を引き算する
   前提 計算機に <最初の入力><次の入力> を入力した
   もし マイナスボタンを押した
   ならば 結果として <計算結果> が表示される

例:
| 最初の入力 | 次の入力 | 計算結果 |
| 10    | 5       | 5       |
| 50    | 1       | 49    |
| 5    | 10    | -5    |

上記の「シナリオアウトライン」部分に記載された内容の中で、”<>”で囲まれた部分が、後続の「例:」に記載されたテーブル上の対応列の内容で置き換えられ、別々のシナリオとして解釈される。
上記の記載例ならば、例えば「例」の1行目については、以下の普通シナリオと同様に扱われる。
シナリオ: 二つの値を引き算する
   前提 計算機に 10 と 5 を入力した
   もし マイナスボタンを押した
   ならば 結果として 5 が表示される

※「例:」に3行分のデータがあるので、SpecFlowでのテスト実行時には、3つ分のシナリオとして扱われる。

コード例:

シナリオアウトラインをテストするコードについても、普通のシナリオのそれと変化はない。

[Given(@"計算機に (.*) と (.*) を入力した")]
public void 前提計算機にとを入力した(int p0, int p1)
{
// 実装は省略
}

[When(@"マイナスボタンを押した")]
public void もしマイナスボタンを押した()
{
// 実装は省略
}

[Then(@"結果として (.*) が表示される")]
public void ならば結果としてが表示される(int p0)
{
// 実装は省略
}

SpecFlowでの実行時には、以下のように、3つのシナリオとして別々に実行される。


背景


複数のシナリオにおいて、共通の前提条件(満たすべきデータとか、ユーザの種類とか)が存在する場合に便利な記載方法。

記載例:


機能: ユーザ管理
   ユーザの追加機能。管理者のみが利用できる。
   全シナリオ共通の「背景」のサンプル。

背景:
   前提 管理者としてログインしている
     かつ システムに以下のユーザが存在する
     | ユーザID | ユーザ名   |
     | TARO1 | 一般 太郎  |
     | JIRO2 | 一般 次郎  |
     | ADM3  | 管理者 三郎 |

シナリオ: ユーザを追加する
   もし 以下のユーザを追加した
     | ユーザID | ユーザ名   |
     | SIRO1 | 一般 四朗  |
   ならば システムに存在するユーザは以下の通りとなる
     | ユーザID | ユーザ名   |
     | TARO1 | 一般 太郎  |
     | JIRO2 | 一般 次郎  |
     | ADM3  | 管理者 三郎 |
     | SIRO1 | 一般 四朗  |

シナリオ: ユーザを削除する
   もし ユーザ 一般次郎 を削除した
   ならば システムに存在するユーザは以下の通りとなる
     | ユーザID | ユーザ名   |
     | TARO1 | 一般 太郎  |
     | ADM3  | 管理者 三郎 |


上記記載例で「背景:」として記載された内容が、続いて記載されている二つのシナリオ双方に適用される。上記の試験実行時には、二つのシナリオそれぞれで、「背景」に記載された内容が実行される。

コード例:


コードは、普通のシナリオと差はない。

[Given(@"管理者としてログインしている")]
public void 前提管理者としてログインしている()
{
    // 実装は省略
}

[Given(@"システムに以下のユーザが存在する")]
public void 前提システムに以下のユーザが存在する(Table table)
{
    // 実装は省略
}

[When(@"以下のユーザを追加した")]
public void もし以下のユーザを追加した(Table table)
{
    // 実装は省略
}

[When(@"ユーザ 一般次郎 を削除した")]
public void もしユーザ一般次郎を削除した()
{
    // 実装は省略
}

[Then(@"システムに存在するユーザは以下の通りとなる")]
public void ならばシステムに存在するユーザは以下の通りとなる(Table table)
{
    // 実装は省略
}


試験実行結果例:

各シナリオには「もし」と「ならば」しか記載されていないが、SpecFlowでの試験実行時には、各シナリオの「もし」部分の実行前に、「背景」に記載された「前提」が2つ、実行されるのが確認できる。



かつ、しかし


「前提」「もし」「ならば」の部分に、複数の条件、動作を記載したい場合には、以下のようにかける。

記載例:


機能:都知事選

シナリオ: 都知事になる
前提 日本国民
かつ 被選挙権がある
かつ 人気がある
かつ 実力がある
しかし 犯罪をおかしていない
もし 都知事選に立候補する
 かつ 法律を守る
ならば 都知事になれる
しかし 苦労する

これは、以下のように記載するのと同じである。

機能:都知事選

シナリオ: 都知事になる
前提 日本国民
前提 被選挙権がある
前提 人気がある
前提 実力がある
前提 犯罪をおかしていない
もし 都知事選に立候補する
もし 法律を守る
ならば 都知事になれる
ならば 苦労する

コード上でも、二つの記載方法で差は出ない。


補足:同義語の一覧

いくつかの用語について、どちらで記載しても問題ない同義語が存在する。
一覧は、以下の通り:
英語
日本語
同義語
and
かつ

background
背景

but
しかし
但し, ただし
examples
サンプル
feature
フィーチャ
機能
given
前提

scenario
シナリオ

scenarioOutline
シナリオアウトライン
シナリオテンプレート, テンプレ, シナリオテンプレ
then
ならば

when
もし


SpecFlow が動作する環境が整っているのであれば、入れ替えてみて、実際に動作に差分が無いのを確認してみていただきたい。

大体のフィーチャの記載方法については、以上で問題ないはず。
SpecFlowは、Cucumberの「Gherkin」のルールに従っているので、Cucumberでも上記の記載ルールはそのまま使用できると思われる。

以上。

0 件のコメント:

コメントを投稿