こんにちは、Webエンジニアのほんまです。
ユニファのAdvent Calendar 2019も21日目ですね。 昨日は、弊社インフラエンジニアのすずきによるAWS Lambda Provisioned Concurrencyに関するエントリでした。 25日間、連続で記事を書き続けられるほどメンバーが集まったんだなぁと思うと、感慨深いものがあります。
それでは本題に入っていこうと思います。 先日、私が所属するチームで担当したルクミーフォトのリニューアルでの変更点を紹介しました。
このエントリ内で「フロントエンドではVue.jsを使用したシングルページアプリケーション(SPA)になっている」と紹介しています。 一方、Rails 5.1からは System Test が導入されており、E2E(End To End)の画面レベルでのテストを容易に実現できるようになっています。 画面内で実行されるJavaScriptにも対応しており、この機能を使った自動テストをやってみたいなーと思っていました。
ここで1つ疑問がありました。 「Vue.jsを使ったSPAのアプリケーションも、RailsのSystem Testでテストできるのだろうか?」ということです。
今後、画面レベルでのテストも自動化していきたいと考えており、この機能がリニューアル後のルクミーフォトでも利用可能なのか、今回試してみようと思います。
テスト対象
今回、ブログ用にすごく簡単なRailsアプリケーションを作成しました。 以下のURLにおいてありますので、cloneしてローカルで試すこともできます。
https://bitbucket.org/unifa-public/rails_system_test_with_vue/src/master/
ざっとアプリケーションの説明をします。
- トップページにアクセスすると、ログイン画面が表示される。
- SPAなので、この時点でHTMLやJSを全て送信し、これ以降のサーバーへのアクセスは、全てAPIを通して行う。
- 「ユーザーID」、「パスワード」が空の状態で「ログインする」ボタンを押した場合、エラーメッセージが表示される。
- この処理は、全てフロントエンドのVue.jsで行なっている。
- 間違った「ユーザーID」、または「パスワード」を入力して「ログインする」ボタンを押した場合も、エラーメッセージが表示される。
- この処理はサーバーサイドでチェックを行い、エラーメッセージの表示のみフロントエンドで行なっている。
- 正しい「ユーザーID」、「パスワード」を入力して「ログインする」ボタンを押した場合、マイページ画面へ遷移する。
- このページ遷移も vue-router を用いて、フロントエンドで行なっている。
- マイページ画面には、現在ログインしているユーザーの「名前」を表示する。これは、以下のマイページ画面コンポーネントの初期化処理(createdイベント内)において、以下の処理を実現することで実現している。
- (フロントエンド)ログインユーザーの情報取得APIを呼ぶ。
- (バックエンド)セッションから現在ログイン中のユーザーをDBから取得し、その情報を返す。
- (フロントエンド)APIから返された情報を使用して、「名前」を表示する。
ミニマムではありますが、SPAにありがちな処理を一通り網羅できているのかな、と思ってます。 このアプリケーションに対して、E2Eのテストを実行してみたいと思います。
テスト実行
テストコード
こちらが作成したテストコードになります。 capybara を使用して記述します。英文として自然に読めるようなDSLになっているため、どんな操作をして何を確認するか、なんとなく理解できるのではないでしょうか。
require 'application_system_test_case' class LoginsTest < ApplicationSystemTestCase test 'login process' do # テストユーザーを作成。テスト終了後に自動で削除される。 user = ::User.create!(uid: 'uid', password: 'password', name: 'name') visit root_url assert_selector 'h1', text: 'ログイン画面' # ユーザーIDが空 fill_in('uid', with: '') fill_in('password', with: 'password') click_button('ログインする') assert_text 'ユーザーIDを入力してください。' # パスワードが空 fill_in('uid', with: 'uid') fill_in('password', with: '') click_button('ログインする') assert_text 'パスワードを入力してください。' # ユーザーIDが間違っている fill_in('uid', with: 'wrong_uid') fill_in('password', with: 'password') click_button('ログインする') assert_text 'ユーザーID、またはパスワードに間違いがあります。' # パスワードが間違っている fill_in('uid', with: 'uid') fill_in('password', with: 'wrong_password') click_button('ログインする') assert_text 'ユーザーID、またはパスワードに間違いがあります。' # 正しいユーザーIDとパスワード fill_in('uid', with: user.uid) fill_in('password', with: user.password) click_button('ログインする') # ログイン後の画面の確認 assert_current_path('/mypage') assert_selector 'h1', text: 'マイページ' assert_text user.name end end
テスト環境
テストの実行環境も簡単に変更できるようです。
test/application_system_test_case.rb
で設定します。
require "test_helper" class ApplicationSystemTestCase < ActionDispatch::SystemTestCase driven_by :selenium, using: :chrome, screen_size: [512, 512] end
スクリーンサイズはもちろん、 :using
オプションを :firefox
や:headless_chrome
に変更することで、ブラウザを変えたり、ヘッドレスモード(ブラウザを画面に表示しない)での実行も簡単にできます。
実行結果
Chromeを使って、テストを実行している様子です。 こちらわかりやすさのため、 テストコード1行ごとに1秒sleep を入れています。sleepを外すと3,4秒ほどで完了してしまいます。
こちら画面が小さくてわかりづらいのですが、 無事成功 してます🎉
Railsすごい!!Vue.jsで動かしている部分も問題なく動作&検証できています。
失敗時の挙動
失敗時の挙動も確認してみます。 今回は、ログイン画面を開いた時のタイトルを変更してみました。
<template> <div> - <h1>ログイン画面</h1> + <h1>テスト失敗画面</h1> <form @submit.prevent="onSubmit"> <div>
これでテストを実行してみます。
無事失敗しました🎉
しかし、驚きはここからです。 コンソールに表示されている以下の一文に注目です。
[Screenshot]: /Users/xxxxxxxxxx/tech-blog/rails_system_test_with_vue/tmp/screenshots/failures_test_login_process.png
このファイルを開いてみると...
なんと テスト失敗時の画面のスクリーンショット が自動で取得されています! これはテスト失敗の原因究明時に役立ちそうです。ありがたや〜。
まとめ
今回、Rails + Webpacker + Vue.jsの構成でSystem Testが動くか確認してみました。
結論としては、無事動作することを確認できました🎉 実際のアプリケーションではもっと複雑な処理や画面なため、もしかすると正しく動かないケースもあるかもしれませんが、基本的な処理では問題なさそうです。
画面レベルのテストは人手に頼りがちなため、こうやって自動テスト化できると工数面でのメリットが大きそうだなと感じています。 今後は弊社QAチームと連携しつつ、E2Eテストを動かす候補の1つとして加えたいと思っています。
最後までご覧いただきありがとうございました🙇♂️何かの参考になったら幸いです。
明日は、同じく弊社Webエンジニアのかきもとが担当です。 最近リリースされたGitHub Actionsに関する記事になるみたいですよ。