対象者
- Cypressを使ったテストを書いたことがある人
- RailsのRSpecを使ったテストを書いたことがある人
手動テスト辛い
私が担当している案件の中には、1年以上運用しているwebサービスがあります。
フロントエンド・バックエンド共に存在します。
課題は色々あるのですが、一番課題に感じているのがテストです。
そのサービスの前回の機能追加リリース時は、画面操作系のテストは手動で行いました。
そうなってしまった理由は大きく分けて以下3点です。
- 画面操作系の自動テストを書く時間がない
- 手動テストした方が早いという考えがある
- 今の画面操作系自動テストの仕組みでは画面操作後のDB値チェックができない
この問題をなんとか解決したいという思いがあります。
単体テストが一番重要
まず大前提の話です。
単体テストとは、関数やメソッド単位での機能が期待通りであるかのテストです。
これまで色々とバグや障害に遭遇してきた訳ですが、振り返ってみるとほとんど単体テストをやっていれば事前に解決できるものばかりです。
私が以前読んだ以下本には、それはそれはもう過剰なくらい単体テストの重要性が書かれています。
ソフトウェア品質を高める開発者テスト 改訂版 アジャイル時代の実践的・効率的でスムーズなテストのやり方
- 単体テストで9割のバグ・障害はなくすことができる
- 自動テストコードを書き、プロジェクト終盤のテストフェーズではなく開発フェーズでバグを潰す
という趣旨の話が書いてあります。
私の経験上、その考え方には賛成です。
これに関しては、単体テストをちゃんと書け、といった話です。
しかし、単体テストだけでは、カバーしきれない部分は確かに存在します。
画面操作系テストも重要
ここからが本題です。
単体テストが大事と言いつつ、これまでのバグ・障害で、画面操作系テストを通してでしか検知できないものがありました。
(単体テストで検知できる障害に比べて割合は小さいですが)
例えば、画面で選択した値とDBに入った値が一致しているかテストするケースです。
私が今一番課題に感じているのは画面操作系テストの自動化です。
手動テストを無くし、自動テスト化するためには冒頭で述べた通りの障壁があります。
- 画面操作系の自動テストを書く時間がない
- 手動テストした方が早いという考えがある
- 今の画面操作系自動テストの仕組みでは画面操作後のDB値チェックができない
これをどう打開していくか?を考える必要があります。
CypressOnRailsでは辛いところがある
現状画面操作系テストのためにCypressを導入しています。
また、DBに対して、テストデータをテストケース単位で設定できるようにするため、
CypressOnRailsを導入しています。
しかし、以下課題があります。
- テストデータ生成部分のコードに書き辛さを感じる
- 画面操作後DBの値が適切か?他ユーザーのデータに意図せぬ更新が入らないか?をテストする事ができない
テストデータ生成部分のサンプルコードはこちらです。
cy.appFactories([
['create', 'user', {email: 'test@test.com', password: 'testtest'}],
['create', 'period', {published: true}]]).then((periods: Period[]) => {
cy.appFactories([['create', 'end_user']]).then((end_users: EndUser[]) => {
uid = end_user[0].uid
cy.appFactories([['create', 'entry', {period_id: periods[0].id, status: 'winner_pushed', work_id: 1, detail_of_work_id: 1, end_user_id: end_users[0].id}]]).then((entries: Entry[]) => {
entry = EntryFactory.create(entries[0])
cy.appFactories([['create', 'timetable', {target_date: periods[0].box_start_date, period_id: periods[0].id}]]).then((timetables: Timetable[]) => {
timetable = TimetableFactory.create(timetables[0])
cy.appFactories([['create', 'reservation', {target_date: timetables[0].target_date, entry_id: entries[0].id, timetable_id: timetables[0].id}]]).then((reservations: Reservation[]) => {
reservation = ReservationFactory.create(reservations[0])
})
})
})
})
})
関連レコードを作成するために、.thenを連発しないといけないので、見にくいです。
RailsのFactoryBotを間接的に使えるようにしている事は良いのですが、書き方が..。
SystemSpecが良い
Cypress導入当初は、画面のテストでしょ?という事で、当時話題となっていたCypressを導入しました。
しかし、画面操作前のDBテストデータ生成や画面操作後のDBの値テストの観点がありませんでした。
この観点で考えると、やはりSystemSpecが良いと考えます。
- 純粋なFactoryBotの書き方でテストデータ生成ができる
- 画面操作後DBの値が適切か?他ユーザーのデータに意図せぬ更新が入らないか?をテストできる
サンプルコードはこちらです
require 'rails_helper'
RSpec.describe 'UserRegistrations', type: :system do
before do
Capybara.app_host = 'http://localhost:8080'
end
# 純粋なFactoryBotの書き方で別のユーザーを生成
let!(:other_user) { create(:user, email: 'other@example.com') }
it 'creates a new user and checks the database' do
visit '/signup' # ユーザー登録ページへのパスを指定
# 画面の入力フォームに値を入力する
fill_in 'Email', with: 'newuser@example.com'
fill_in 'Password', with: 'securepassword'
fill_in 'Password confirmation', with: 'securepassword'
click_button 'Sign up'
# データベースにユーザーが保存されているか確認
user = User.find_by(email: 'newuser@example.com')
expect(user).not_to be_nil
expect(user.email).to eq('newuser@example.com')
# 属性に関してDB値チェック可能
expect(user.some_other_attribute).to eq('Expected Value')
# other_userの属性が変更されていないことを確認
other_user.reload # データベースから最新の情報を再読み込み
expect(other_user.email).to eq('other@example.com')
end
end
PlaywrightをRailsに導入する
Rails では Capybara というライブラリを経由して自動操作ツールを呼び出すことが多いです。代表的なものはseleniumです。そしてPlaywrightもその内の一つであり、Rails7.1から使えるようです。
Playwrightの公式は以下です。
Microsoftが開発しており、近年人気で出てきているようです。
なぜPlaywrightが良いのか?については、
Playwright の Ruby クライアントの開発者である Yusuke Iwaki さん(@yi01imagination)のスライドがわかりやすいです。
Capybaraのドライバーにseleniumを使った場合、DOM変更検知が弱くテストが不安定という問題があったようですが、Cypressと同様に、Playwrightでもこの問題に対する対処がなされているようです。
結論
SystemSpecを使えばテストデータ生成の書き方も簡単になりますし、DBチェックもできます。
よって冒頭に述べた以下問題に対してある程度対処ができるのではないか?と考えています。
- 画面操作系の自動テストを書く時間がない
- 手動テストした方が早いという考えがある
- 今の画面操作系自動テストの仕組みでは画面操作後のDB値チェックができない
最後に
自分が携わるプロジェクトにおいて、画面操作系テストの質を上げていきたいと思った時に、CypressOnRailsは最適ではないと感じます。
既存のプロジェクトの画面操作系テストを全て書き換えるのは中々に困難ですが、機能追加・改修時に書き換えるなどで対応していかないと手動テスト工数が減らないなぁと感じています。
Capybaraでもカスタムデータ属性が使用できるみたいなので、テストコードの改修だけでいけそうなのはまだ救いなのかもしれない..(find('[data-cy="login-button"]')
で要素指定ができる)
もちろん新規プロジェクトでは、SystemSpecのCapybaraでPlaywrightを使っていきたい気持ちです。
以上です。