DockerでCucumberのQuickstart環境を作ってみた

はじめに

BDDなe2eテストと言えばやはりCucumberですよね。

Gherkinを使って自然言語のようにテストケースが書け、具体的なステップをRubyJavaで書けるので非常に便利です。

今回、久方ぶりにCucumberを使う必要がありそうなので便利そうな設定をいくつか入れてDocker化しておきました。これでRubyとかが入ってない環境でも問題なく利用できます。

今回実施してる設定は以下の通り

  • ChromeのHeadlessモードを使ったテスト
  • デバッグ用にオプションで指定したらChromeGUIモードで起動するように設定
  • テストに失敗したら自動的にスクリーンショットをとってテストレポートに含めるように設定
  • Dockerコマンドのラッパースクリプトとしてqcmbコマンドの作成

以下のGitHubリポジトリに置いてあるのでCloneすれば使えます。Chromeを使ってるのでJSやCSSを実際と違う形で解釈したりレンダリングしないので安心感がありますよね。

github.com

使い方

テストの実行

とりあえずcucumberを実行するなら以下のコマンド。featuresディレクトリ配下のテストを実行.

qcmdは単にDockerのラッパーコマンドで実際はdocker run -it --rm -vpwd:/usr/src/app koduki/cucumber cucumberを実行しています。

$ ./qcmb

標準出力ではなくHTMLでレポートが欲しい時は下記のようにCucumberのコマンドを実行。出力先はreportを前提にいくつかの設定がされているので注意。

$ ./qcmb --format html --out report/index.html 

テストが失敗した時のスクリーンショットも上記のreportディレクトリに出力されます。

ディレクトリを削除したい時はcleanを実行

$ ./qcmb clean

GUI mode で起動

CI/CDで利用することを考えるとヘッドレスモードがベストなのですが、たまにデバッグGUIモードでChromeを立ち上げたい事もあります。

DockerはCLIベースの環境ではありますが、LinuxなのでX11をホスト側に飛ばすのは簡単にできます。

Macでの準備

MacX11とProxyをインストールします。

$ brew install socat
$ brew cask install xquartz

Xquartzを起動してXteam を xquartz上で開きます。

open -a XQuartz 

パーミッションを許可してProxyをXteam上で起動します。

$ xhost + 
$ socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:/tmp/.X11-unix/X0

qcmbコマンドの--Xheadless=falseオプションを指定して起動します。スクリプトの中で環境変数DISPLAYにホストマシンのIPアドレスなどを連携しています。

$ qcmb --Xheadless=false

設定の解説

support/env.rb

Docker内だとChromeを実行しようとするとDevToolsActivePort file doesn't existが出ます。そのため--no-sandbox を有効にする必要があります。 また、環境変数に応じてHEADLESSモードとGUIモードのそれぞれが選べるようにしています。

require 'capybara/cucumber'
require 'webdrivers'

Capybara.register_driver :docker_chrome_headless do |app|
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
  browser_options.args << '--headless'
  browser_options.args << '--no-sandbox'
  browser_options.args << '--disable-gpu'
  browser_options.args << '--window-size=1920,1080'
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    options: browser_options
  )
end

Capybara.register_driver :docker_chrome do |app|
  browser_options = ::Selenium::WebDriver::Chrome::Options.new
  browser_options.args << '--no-sandbox'
  browser_options.args << '--disable-gpu'
  browser_options.args << '--window-size=1920,1080'
  Capybara::Selenium::Driver.new(
    app,
    browser: :chrome,
    options: browser_options
  )
end

def driver
  is_no_headless = ENV["CHROME_NO_HEADLESS"]
  if is_no_headless then :docker_chrome else :docker_chrome_headless end 
end

Capybara.configure do |config|
  config.default_driver    = driver()
  config.javascript_driver = driver()

  config.app               = nil   
  config.run_server        = false
end

support/screenshot.rb

Cucumberのテストが失敗したときに自動でスクリーンショットをとってテストレポートに埋め込むようにしています。

そもそもsupport配下にあるRubyスクリプトは名前に関係なく読み込まれます。これを利用してこの中にフックポイントであるAfterを追加してシナリオ終了後の動作を記述します。 あとは見た通りですがシナリオが失敗したときに、スクリーンショットをとってレポートに埋め込んでいます。

After do |scenario| 
    if scenario.failed? 
        path = "report/#{scenario.__id__}.html"
        page.driver.browser.save_screenshot(path)
        embed(path, "image/png")
    end 
end%

例: テストレポートとスクリーンショット

f:id:pascal256:20200507092702p:plain

まとめ

かなり久しぶりのCucumberだったので結構調べながら作りました。

Cucumberは10年経ってもやはり現役で多少は競合はあるものの圧倒的なシェアは流石ですね。Gherkinは仕様記述言語とまでは言えないもののログインとかの動作の意味が厳密に定義されるので自動テストをしなくてすら有用な文法だと思います。この手のツールを使ってテストケースと実際の自動テストの実行の解離が小さくなることがやはり良いですよね。

それではHappy Hacking!

参考リンク