こんにちは。大阪開発部の岡田(@y_okady)です。大阪のランチは今日も安くて旨いですね。
サイボウズと言えばSeleniumを使ったE2Eテストのイメージが定着しつつありますが、そのE2Eテストをさらに強固なものにするために新しい仕組み作りにチャレンジしました。
作り始めて3日しか経っていないプロトタイプの段階ですが、構成はだいたい固まってそれなりに動くものができたのでご紹介します。
きっかけ
2週間ほど前に読んだ「エッセンシャルスクラム」に、こんなことが書いてありました。
テストはスプリント期間よりも長くかかる場合がある。 そうなるのは、開発チームが巨大な手動テストという負債を背負っているからだ。
サイボウズではE2Eテストの自動化が進んでいると言っても、自動化できているのは試験全体のほんの一部です。ほとんどのテストは手動で実施しており、プログラマが1ヶ月かけて開発したものをQAが1ヶ月かけて手動でテストしています。
この巨大な手動テストという負債を無くすことができれば、より良い開発ができるんじゃないか。これはもうやるっきゃない!
要件
プログラマにとってもQAにとってもメンテナブルであること、これが一番の要件です。他にもいろいろあるのですが、ここでは割愛します。
構成要素
Turnip
https://github.com/jnicklas/turnip
Cucumberの後継として期待されているE2Eテストフレームワークです。RSpec内でテストを実行します。 自然言語でシナリオ(テストしたい一連のステップ)を記述します。
QAがステップを組み合わせてシナリオを設計し、プログラマがステップの具体的な処理を実装する、といった役割分担が可能となります。
SitePrism
https://github.com/natritmeyer/site_prism
Seleniumのデザインパターンのひとつであるページオブジェクトパターンを簡単に使えるDSLです。 DRYで再利用しやすいテストを書くことができます。
Turnip Formatter
https://github.com/gongo/turnip_formatter
Turnipの実行結果をきれいに出力してくれるformatterです。美しさは正義です。
YARD::Turnip
https://github.com/takaishi/yard-turnip
Rubyのドキュメント生成ツールYARDが生成するドキュメントに、Turnipのステップを含めるアドオンです。 ソースコードを見なくても実装済みのステップを簡単に確認できます。
Sinatra
軽量Webアプリケーションフレームワークです。kintoneからRSpecを呼び出したり、Turnipの実行結果やYARDのドキュメントを閲覧するために使用します。
kintone
我らがkintoneです。JavaScriptカスタマイズを利用することで、kintoneに登録されたデータを外部のWebアプリケーションに簡単に送信できます。
プロトタイプができるまで
Gemfile
source "https://rubygems.org" gem "rspec", "3.3" gem "turnip", "1.3.1" gem "site_prism" gem "turnip_formatter", "0.3.4" gem "selenium-webdriver" gem "chromedriver-helper" gem "yard" gem "yard-turnip" gem "sinatra"
Turnip Formatter(0.3.4)がRSpec(3.3)、Turnip(1.3.1)じゃないとちゃんと動かず、ちょっとハマりました。
RSpecとTurnipの設定
RSpecでTurnipを実行するためのオプションと、実行結果をTurnip Formatterで出力するための設定を .rspec
に記述します。
-r turnip/rspec -r turnip_formatter --format RSpecTurnipFormatter --out web/public/report.html
spec_helper.rb
に、Turnipの各種設定を記述します。今回はとりあえず、ChromeでSeleniumを実行するようにしています。
require "capybara/dsl" require "capybara/rspec" require "turnip" require "turnip/capybara" require "site_prism" Dir.glob("spec/pages/**/*page.rb") { |f| load f, true } Capybara.default_driver = :selenium Capybara.run_server = false Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, browser: :chrome) end
SinatraアプリからRSpecを実行
SinatraアプリでHTTPリクエストを受け取ってRSpecを実行できるようにします。kintoneからHTTPリクエストを送れるように、HTTPレスポンスヘッダーに Access-Control-Allow-Origin: *
を指定しておきます。
require "sinatra" set :environment, :production post "/feature" do headers \ "Access-Control-Allow-Origin" => "*" File.write("tmp.feature", params[:feature]) system("rspec tmp.feature") end
kintoneからSinatraアプリにHTTPリクエストを送信
シナリオを登録するkintoneアプリを作成して、JavaScriptカスタマイズでシナリオをSinatraアプリに送信できるようにします。
サンプルプログラムは割愛しますが、登録したシナリオをTurnipが解釈できるGherkinという形式に変換して、ボタンクリックで jQuery.post
でHTTPリクエストを送信し、処理が完了したら実行結果を別タブで開くだけの簡単なプログラムです。
たとえば、kintoneにログインして非公開スペースを作成し、別のユーザーでログインしてそのスペースが見えていないことを確認するシナリオは、次のように記述できます。
Feature: スペース機能 Scenario: 非公開スペースを作成する * user1 / user1 でログインする * [Portal] スペースを作成する | スペース名 | 非公開 | | サンプルスペース | x | * ポータルアイコンをクリックする * "サンプルスペース" と表示されている * ログアウトする * user2 / user2 でログインする * [Portal] "すべてのスペース" のスペース一覧を表示する * "サンプルスペース" と表示されていない
なお、GherkinではGiven, When, Thenなどのキーワードを利用するのが一般的ですが、真面目に書くのは大変なので今回は * を使っています。
https://github.com/cucumber/gherkin/blob/master/lib/gherkin/i18n.json#L422-L435
ページオブジェクトとステップの実装
SitePrismを使ってページオブジェクトを実装します。Webアプリケーションのひとつの画面がひとつのクラスになります。 そして、Gherkinで記述されたステップの処理を記述します。
次のサンプルプログラムは、ログイン画面のページオブジェクトと、ログインのステップを記述したものです。 ログイン画面にはユーザー名とパスワードの入力欄と、ログインボタンがあります。 ログインのステップでは、指定されたユーザー名とパスワードを入力欄にセットし、ログインボタンをクリックします。
# ログイン画面のページオブジェクト class LoginPage < SitePrism::Page set_url "<kintoneのURL>" element :username, "input[name='username']" element :password, "input[name='password']" element :submit, "input.login-button" end # ログイン画面のステップ step ":username / :password でログインする" do |username, password| @page = LoginPage.new @page.load @page.username.set username @page.password.set password @page.submit.click end
プログラマは、開発中にページオブジェクトとステップをメンテナンスします。 これらをしっかりメンテナンスし続けることが、E2Eテストにおけるプログラマの役割です。
実装済みステップのドキュメント生成
シナリオを記述するQAが実装済みステップを確認できるようにするために、YARDを使ってドキュメントを生成します。
$ yard -o web/public/doc --plugin yard-turnip **/*page.rb
できたもの
前述の、kintoneにログインして非公開スペースを作成し、別のユーザーでログインしてそのスペースが見えていないことを確認するシナリオを実行してみました。
まとめ
E2Eテストはメンテナンスが大変で、続けるのが難しいという話をよく耳にします。 サイボウズではみんなが歯を食いしばってメンテナンスを続けていますが、やっぱり大変です。 そんな状況の中で、手動でやってるテストを全部自動化したいなんて言ったら袋叩きにされるでしょう。
まだ間に合うかもと思った方、すぐにメンテナブルなE2Eテストを導入してください。
もう間に合わないと思った方、袋叩きを恐れずに導入を検討してください。
メンテナブルなE2Eテストを導入して、高品質なWebアプリケーションをこれからもずっと開発し続けましょう!