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 Expectations、RSpec 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への置き換えは思っていたより少ない時間で終わりました。そしてテストの実行時間を計りました。
ツール | 実行時間 |
---|---|
Cucmber | 6分41秒 |
Turnip | 4分39秒 |
本格的な置き換え前に1つのシナリオでテストした際には50%程度になったのですが、実際には30%程度の短縮でした ^^);
しかし、GitHub Actionsの方は、完璧ではありませんがE2Eテストが通せるようになりました!
今後
現在はCapybaraのドライバーにはPhantomJS(poltergeist)を使っていますが、PhantomJSはサポートが終了しています。今後はPhantomJSのかわりに、Seleniumを使うか、またはCuprite。そもそもCapybaraではなくPuppeteer?Playwright?・・・ などの移行を考えないといけません。
しかし、Turnipベーズに変更したので、これらの移行もやりやすくなったと思います。