EY-Office ブログ

E2EテストをCucumberからTurnipに変更した

2009年から開発・メンテナンスしているECサイトは2009年の終わり頃からCucumberをE2Eテストに採用してきました。当時はTDD(テスト駆動開発)やBDD(ビヘイビア駆動開発)の黎明期で、Ruby on Rails用に実用的なE2Eテストが書けるのはCucumberくらいしかありませんでした。

その後Ruby on RailsやCucumber自身のバージョンアップに付いて行きましたが、E2Eテストの実行時間が遅くなりて行きました。極め付けは開発環境をDockerにしたことで実行時間は6分を超えるようになりました。
またGitHub ActionsでCIが実行できるようになったのですが、Cucumber使ったE2Eテストは途中でエラーになってしまいCIではE2Eテストが実行できませんでした。

大きな蕪

Cucumber

Cucumberをご存じない方むけのCucumberの解説です。 Cucumberの大きな特徴はテストのシナリオを自然言語風のGherkinで書ける事です、以下がテストシナリオの例です。

  • シナリオ
  シナリオ: 注文手続きへを開始し、ログインし送り先を選択すると、正しい注文確認画面が表示される
    前提 カートに商品を入れてお買い物内容ページを表示している
    かつ カード情報が取得出来るように設定

    もし "ご注文手続きへ"ボタンをクリックする
    かつ "メールアドレス"に"yy@ey-office.com"と入力する
    かつ "パスワード"に"XXXXXXX"と入力する
    かつ "ログイン(お届け先確認)"ボタンをクリックする

    かつ "ご注文手続きへ"ボタンをクリックする
    かつ "お届け先1 (ご登録住所)"を選択する
    かつ "お届け日時選択へ"ボタンをクリックする
    かつ "お支払い方法選択へ"ボタンをクリックする

    かつ "代金引換"を選択する
    かつ "ご注文内容確認へ"ボタンをクリックする

    ならば "ご注文確定ボタンを押して下さい"と表示されていること
    かつ "合計"表示欄に"¥40,000"と表示されていること

Cucumberはこの自然言語風のシナリオをAI等を使って解釈するのではなく、以下のようなStepsに定義されている正規表現にマッチするStepが実行され画面UIの操作や画面表示確認の処理が実行されます。

  • Steps
When /^"([^"]*)"ボタンをクリックする$/ do |button|
  click_button(button)
end

When /^"([^"]*)"に"([^"]*)"と入力する$/ do |field, value|
  fill_in(field, with: value)
end

When /^"([^"]*)"と表示されていること$/ do |text|
  expect(page).to have_content(text)
end

画面UIの操作や画面情報の取得にはCapybaraが使われています。
確認(テストコード)にはRSpecが使われています、またRSpecのモック(Mocks)も使う事ができます。

CucumberはRSpec的な記述が使えますが、RSpec ExpectationsRSpec Mocksを含む独自ツールで、設定ファイル等には独自の世界観を持っています。

Feature specs

当初RSpecはユニットテスト相当のツールでしたが、Rspec 2.13でFeature specsと呼ばれるE2Eテスト機能が追加されました。これはRSpecにCapybaraを組み合わせたものです。Rails用のRSpec Railsと組み合わせると、Ruby on RailsのE2Eテストが書けるようになります、以下は簡単なFeature specsの例です。

feature "お知らせ管理" do

  scenario "編集すると、トップページが変更される" do
    within "form" do
      fill_in "お知らせ本文", with: "10年に一度の20%値下げのお知らせ"
      click_on "更新"
    end

    visit "/"
    expect(page).to have_text "10年に一度の20%値下げのお知らせ"
  end
end

Feature specsは完全なRubyのコードのみでプログマーにとっては書きやすいです。CucumberはシナリオとStepsに分かれているため、その間での不整合からエラーを起こしがちで苦労しました。

ただし、Cucumberのシナリオ(Gherkin)はプログラマーでなくても読む事ができるので、プログラマーとユーザーの間にSE(情シス、SIer)が入っていたりする場合はCucumber(Turnip)は、すんなりと受け入れられる事があります。

Trunip

TruipはFeature specs(RSpec)をベースにCucumberを再構築したもです。テストシナリはGherkinで書きます、Stepsも似ていますが正規表現ベースではありません。

  • シナリオ
  シナリオ: 注文手続きへを開始し、ログインし送り先を選択すると、正しい注文確認画面が表示される
    前提 カートに商品を入れてお買い物内容ページを表示している
    かつ カード情報が取得出来るように設定

    もし "ご注文手続きへ" ボタンをクリックする
    かつ "メールアドレス" に "yy@ey-office.com" と入力する
    かつ "パスワード" に "XXXXXXX" と入力する
    かつ "ログイン(お届け先確認)" ボタンをクリックする

    かつ "ご注文手続きへ" ボタンをクリックする
    かつ "お届け先1 (ご登録住所)" を選択する
    かつ "お届け日時選択へ" ボタンをクリックする
    かつ "お支払い方法選択へ" ボタンをクリックする

    かつ "代金引換" を選択する
    かつ "ご注文内容確認へ" ボタンをクリックする

    ならば "ご注文確定ボタンを押して下さい" と表示されていること
    かつ "合計" 表示欄に "¥40,000" と表示されていること
  • Steps
step ':button ボタンをクリックする' do |button|
  click_button(button)
end

step ':field に :value と入力する' do |field, value|
  fill_in(field, with: value)
end

step ':text と表示されていること' do |text|
  expect(page).to have_content(text)
end

Cucumberと似ていますが微妙に違います。しかし、わずかな違いなので簡単なツールを書くことで変換ができます。

問題点は解決したのか?

CucumberからTruipへの置き換えは思っていたより少ない時間で終わりました。そしてテストの実行時間を計りました。

ツール実行時間
Cucmber6分41秒
Turnip4分39秒

本格的な置き換え前に1つのシナリオでテストした際には50%程度になったのですが、実際には30%程度の短縮でした ^^);

しかし、GitHub Actionsの方は、完璧ではありませんがE2Eテストが通せるようになりました!

今後

現在はCapybaraのドライバーにはPhantomJS(poltergeist)を使っていますが、PhantomJSはサポートが終了しています。今後はPhantomJSのかわりに、Seleniumを使うか、またはCuprite。そもそもCapybaraではなくPuppeteer?Playwright?・・・ などの移行を考えないといけません。

しかし、Turnipベーズに変更したので、これらの移行もやりやすくなったと思います。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ