Gaugeのスクリプトを詳しく見てみる

前回の記事でGaugeを触ってみました。

siro-uma.hatenablog.com

今回は前回の記事の中で動かした実際のスクリプトを詳しく見てみましょう。

Specificationを見る

.spec で定義されているものをGaugeではSpecificationと呼びます。名前のとおりですが、ここにはその機能の仕様を記載します。場合によってはユーザーストーリーでも構わないと思います。これがGaugeの世界での1つのテスト実行単位になります。

それではexample.specを例に中身を見ていきましょう。

# Specification Heading                                                                                                                                                                                     

This is an executable specification file. This file follows markdown syntax.
Every heading in this file denotes a scenario. Every bulleted point denotes a step.

To execute this specification, run

    gauge run specs


* Vowels in English language are "aeiou".

## Vowel counts in single word                                                                                                                                                                              

tags: single word

* The word "gauge" has "3" vowels.


## Vowel counts in multiple word                                                                                                                                                                            

This is the second scenario in this specification

Here's a step that takes a table                                                                                                                                                                            
                                                                                                                                                                                                            
* Almost all words have vowels                                                                                                                                                                              
     |Word  |Vowel Count|                                                                                                                                                                                   
     |------|-----------|                                                                                                                                                                                   
     |Gauge |3          |                                                                                                                                                                                   
     |Mingle|2          |                                                                                                                                                                                   
     |Snap  |1          |                                                                                                                                                                                   
     |GoCD  |1          |                                                                                                                                                                                   
     |Rhythm|0          |                                                                                                                                                                                   
  • 1行目は# から始まります。markdown syntaxのh1にあたる # で始まるこの行は、この仕様のタイトルを表記する場所です。

  • 続く3行はこのSpecificationの説明を記載するところ。というより、Gaugeの予約語?を含まない部分はただの文字列として扱われ、その文がそのままテストのレポートに出力されることになるようです。

  • 続いて*で始まる* Vowels in English language are "aeiou".。Gaugeの中で*から始まる文はStepとして扱われます。ただし、今回のように後述予定のScenarioの前にStepを記載した場合、このStepはContext Stepとして扱われます。Context StepはそれぞれのScenarioが実行される前に必ず実行される処理になり、テストのためのPre-conditionを設定することなどが可能になります。

  • Stepの中に記載されている"aeiou"は外部パラメータとして扱われ、Stepの実装とのなる関数への引数となります。Stepの実装については後ほど説明したいと思います。

  • markdown syntax h2に相当する##で始まるこの行は、先程も登場したScenarioと呼ばれるものになります。私の理解ではGaugeの世界では1つのScenarioが一般的な1つのテストケースに相当すると思われます。1つのSpecificationは必ず1つ以上のScenarioを含む必要があるようです。

  • tagsはSpecificationの配下、もしくはScenario配下に定義可能です。定義されたtagsはテスト実行時のフィルターとして利用可能で、特定のタグを持ったSpecification or Scenarioのみを実行するなどの使い方ができます。

  • *から始まるこの行は上述の通りStepとなります。Scenarioの中に記載されているので、このStepはContext stepではなく、通常のStep、つまりテストケースの1つのステップとして扱われます。

  • ## Vowel counts in multiple word は2つ目のScenario(テストケース)で、続く2つの行はこのテストに関する補足の説明です。

  • *で始まるこの行は上記に記載した通りテストのステップになります。ただし今回はその直後にテーブルが続いています。このテーブルはTable Parametersとして扱われ、直前のStepの実装となる関数の引数として扱われ、表の各行毎にそのStepが評価されます。今回の例ではWordの中に母音(vowel)が何個あるかカウントし、Vowel Countと一致するか確認することになるので、Gaugeでは母音が3個、Mingleでは母音が2個、といった具合です。同じような処理を変数を変えて実施したいときに便利に使えそうです。

以上でSpecificationの説明が終わりました。続いてStepの実装について見ていきましょう。

step_impl.py を見る

step_impl.py には実際のテストの処理を行う実装がなどが記載されています。

from getgauge.python import step, before_scenario, Messages

vowels = ["a", "e", "i", "o", "u"]


def number_of_vowels(word):
    return len([elem for elem in list(word) if elem in vowels])


# --------------------------                                                                                                                                                                                
# Gauge step implementations                                                                                                                                                                                
# --------------------------                                                                                                                                                                                

@step("The word <word> has <number> vowels.")
def assert_no_of_vowels_in(word, number):
    assert str(number) == str(number_of_vowels(word))


@step("Vowels in English language are <vowels>.")
def assert_default_vowels(given_vowels):
    Messages.write_message("Given vowels are {0}".format(given_vowels))
    assert given_vowels == "".join(vowels)


@step("Almost all words have vowels <table>")
def assert_words_vowel_count(table):
    actual = [str(number_of_vowels(word)) for word in table.get_column_values_with_name("Word")]
    expected = [str(count) for count in table.get_column_values_with_name("Vowel Count")]
    assert expected == actual


# ---------------                                                                                                                                                                                           
# Execution Hooks                                                                                                                                                                                           
# ---------------                                                                                                                                                                                           

@before_scenario()
def before_scenario_hook():
    assert "".join(vowels) == "aeiou"
  • まずはimport。gauge pythonではお決まりになりますが、必要なモジュールをインポートします。getgauge.pythonpythonで必要なgaugeのモジュールがすべて入っています。今回はstep, before_scenario, Messagesを使います。

  • 次に登場する変数 vowelsと関数number_of_vowels は今回の例ではテストの対象となる部分になります。今回のテストの対象となるnumber_of_vowelsは引数をなるwordの中に何個の母音が含まれているかをカウントする関数になるようです。

  • 続いて実際のテスト部分の実装になります。@stepから続くアノテーション文。ダブルクオートで囲まれている中身を見てもわかりますが、これがSpecificationの中で呼び出していたStepの実装になります。また<>囲われた部分はSpecificationから外部パラメータを与えるための仮引数になります。ここで定義された仮引数が次に続く関数の仮引数にそのまま使われます。

  • 直後のassert_no_of_vowels_inは直前の@stepで定義されたStepの実装部分です。Specificationからは@stepに記載された文字列で呼び出しますが、実際に処理として行われるのは実装部分であるこちらです。この関数では<word><number>を与え、テストの対象number_of_vowelsがその<word>で期待した数<number>の母音を返すかをassertを使って確認しています。

  • 次の@stepとその実装部分では<vowels>given_vowelsがそれぞれ仮引数として定義されています。名前が違うので別物と勘違いしそうですが、同じものです。@step内と実際の関数の仮引数の名前は一致している必要はなく、順序で参照が決まります。

  • また同実装の中にMessages.write_messageという処理を呼び出しています。この処理の中で記載された文字列は、テスト実行後に出力されるテストレポートに記載されます。Specificationでも#*を使わずに文字列を埋め込むことで、同様のことができましたが、こちらのやり方はでは仮引数などを用いて外部パラメータも埋め込むことができるようでうす。Custom Messagesと呼ばれる機能のようです。

  • 最後の@stepでは<table>を用いています。Specificationでも登場したテーブルですが、ここで引数として用いられます。tableから値を取り出す場合はtable.get_column_values_with_nameを使って値を取り出すことができます。

  • 最後の@before_scenario()はGaugeで使えるhookで、@before_scenario()で定義されるこの処理は、名前の通りScenarioの前処理として都度実行されます。Gaugeのhookはこれ以外に、@before_step, @after_step, @after_scenario, @before_spec, @after_spec, @before_suite, @after_suiteがあるようです。

まとめ

Gaugeで必要になる2つのスクリプトについて見てきました。Markdownで記載するということで、自由度はありつつも機能は結構制限されるのかなと思っていましたが、結構色々なことができそうです。

次はPython + seleniumあたりを使って実際のGaugeのテストを書いてみたいと思います。