E2Eをやったことがない初心者のために簡単にテストシナリオを交えながら少し説明してみた(Cypressを使います)
はじめまして
はじめまして。 ofa-chanです。 https://zenn.dev/ojin です。
この記事を書いた経緯ですが、自分は今まで色んな現場にフロントエンジニアとしてJOINしてきていますが、新しく入ったPJでは必ずと言っていいほどE2Eテストがないので、初心者のために簡単に導入のフローを説明したいと思います。難しいことは書いてません。そういうことは専門書や他の優秀な方の記事を読むのが良いでしょう。この記事を読んで頂き、少しでもE2Eテストの敷居が下がり、かつ導入までの感じが掴めたら幸いです。
ただし、ここに書いてあることはあくまでも個人的な意見なので、間違っている可能性がありますし、もしそうならご指摘頂ければ拝見して修正が必要であれば対応したいと思います。
そもそもE2Eテストとは?
End-To-Endテスト(エンドツーエンドテスト)のことを指します。エンドに関しての細かいことを言い出すときりがないので、今回はスタンダードなWebサービスを例にしたいと思います。
テストに関してですが、ある意味ハックのような挙動をテストするのではなく、サービスを実際にユーザが触っていることを仮定してテストシナリオを設計するのが好ましいです。現状自分の場合、QAでやっていることをある程度自動化したいからE2Eを構築してほしい、というオーダーが自分に来ることが多いため、其のように判断しています。
※ブラウザ環境確認用のE2Eテストもあるとは思いますが、一旦この記事では対象外とさせていただき、需要があれば別の記事でご説明したいと思います。
今回のWebサービス例(イメージです)
- FrontEnd: Node.js(React + Next.js)
- BackEnd: Python(Flask)
- Database: PostgreSQL
この構成ですと、ブラウザにアクセスし、フロントエンドからPythonへ何らかのAPIが叩かれる処理を行い、かつデータベースを参照、更新、削除などの処理が走るようなテストができると良いでしょう。つまり、E2Eテストを起動する時は、Node.jsサーバだけでなく、PythonのサーバやPostgreSQLも動かしておく必要があります。
テストシナリオの設計について
簡単にシナリオを作成してみます。
例)プロフィール編集のテストシナリオ
- ログインし、メニューからマイページに移動する
- マイページで、プロフィール編集をする
- ログアウトする
ここで重要なのは、細かいチェックをテストに入れる必要はないということです。
例えばエラーパターンですね。
1. ログインし、メニューからマイページに移動する
ログインをしようとしているので、メールアドレスやパスワードが空欄、不正などでメッセージが変わると思います。このメッセージが変化する、などと言ったケースを複数パターン用意する必要はありません。網羅するようなチェックは特別な理由がない限りUnitテストで確認するべきことでしょう。
ここでは、何種類か発生するエラーパターンのうち、最も発生しやすく、かつAPIからDBアクセスが発生するものを採用するのが好ましいです。大事なのは、エンドツーエンドになっていることです。ここを意識しましょう。フロントエンドだけで完結するようなことをE2Eでチェックするのは工数的・保守性的にも微妙です。
ただし、頻繁に仕様が変わるような機能はE2Eテストに載せないほうが良さそうです。其の場合、マニュアルテストも考慮しても良いかもしれません。
もちろん時間があるならテストパターンを増やしても結構ですが、E2Eテストの実行時間にも影響が出るので何でもかんでもテストに追加すればいい、という方針は少し見直したほうが良いと思われます。
ログイン後、マイページに移動しています。普通に考えたらマイページというものはログインしていないと訪問不可なので、ここのチェックも同時に入っています。
2. マイページで、プロフィール編集をする
訪問したマイページでプロフィール編集を行います。
ここは更新処理が入ってきます。プロフィールの内容によってはSNSの連携解除など何らかの削除処理も入ってくるかと思います。
今回も、先程のログインと同様に自身のサービスで頻度の高い操作をE2Eに落とし込みましょう。
例えば、プロフィール編集でニックネームや自分のBIOなどを変更する人が多かった場合、この操作を落としてしまうと、QA的にも微妙になってくるので、このテストケースを追加します。
ただし、ここでも先程述べたエラーパターンなど細かい部分はUnitテストで巻き取ってあげるのが望ましいです。理由は先程と同様です。
また、先程のログインでも言うべきことではありますが、ログイン後のフォーム処理の方が分かりやすいと思ったのでこちらで説明します。
例えば、プロフィール編集で更新できる項目を以下だと仮定します
- 表示名
- アイコン(画像)
- 生年月日
- 性別
- メールアドレス
- 電話番号
- パスワード
- 自己紹介
- 会社・所属組織
- 出身地
- 居住地
- 卒業した学校
- 趣味
- 自分のウェブサイトURL
- 言語設定(日本語・英語・中国語)
非常に多い項目かと思いますが、サービスによってはさらに多い項目が用意されているかもしれません。
テストでは、この項目のどれかを指定してテキストボックスにテキストを入力、あるいはチェックボックスやプルダウンによる選択かもしれません。
しかし、テストで特定の項目にフォーカスをあてる時に我々はDOMを取得しなくてはいけません。どのように取得するのが良いでしょうか?
ここでは、CypressのBest Practiceを見てみましょう
以下のボタンをクリックするというテストコードを書きたいとして
<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-cy="submit"
>
Submit
</button>
まずは、最悪の書き方で、絶対にやめた方がいいやり方を書きます
cy.get('button').click();
この書き方だとbuttonというものが脈絡なくいきなり出てきているので、どのボタンのことなのか分かりません。もし、一時的に特定できたとしても、将来ボタンが新たに実装されたらこのコードは破壊されてしまいますね。
その次に絶対にやめた方がいいやり方です
cy.get('.btn.btn-large').click();
classに指定されているものを全て指定しているので先程よりは壊れにくいですが、やはりデザインの変更だったり、同じボタンが新たに追加されたらこのコードは破壊されます。
次はできれば避けたほうがいいやり方です
cy.get('#main').click();
id指定なので、他のボタンとかぶってしまうという問題は発生しえないですね。よって、上記2つのやり方と比べたら格段に良いです。ただ、やはりidは変更の可能性があります。というのも、このidはE2Eのために定義されているわけではないからです。E2Eでid指定をしてしまうと、それ以降は、プロダクトコード側を変更する時にE2Eのことを考えなくてはいけません。
次は状況によっては良いやり方です
cy.contains('Submit').click();
確かに、状況によっては良いと思いますが、これもボタン名が変わったりすると破綻しますので、個人的には微妙かと思います。また、一つ落とし穴があり、例えばページ上部に検索の文言があり、かつ「検索」ボタンを指定する場合、ページ上部の文言を取得してしまいます。
最後にベストなやり方です
cy.get('[data-cy=submit]').click();
このやり方が最も良い理由は明白で、他の変更から完全に隔離されているからです。またプロダクトコードを触る時にもスタイルやJSの動作と連動しない、かつこのコードがE2Eで使われていることが分かりやすいです。
また、これは開発支援の話にはなりますが、Playgroundを使った開発時にもこのやり方は最適です。自動的にこのdata-cyを算出します。また、data-cy以外にも、data-test, data-testidなどの書き方が可能です。
/// ↓ 2021.09.24 追記 ↓ ///
Commandsに置き使いやすいようにするのがオススメである
今回のケースでは追加した方が良いが、Commandsは割と肥大化しやすいので、何でもかんでも便利関数じゃん、とならずにちゃんと考えたほうが良い。
Cypress.Commands.add('dataCy', (selector) => {
return cy.get(`[data-cy=${selector}]`);
});
メリットは明確でtypoなどを防げるのと、何より型サポートできる。なお、今回はその様なコードは書いていないのだが、/support/index.ts にdataCyメソッドを定義するだけで良い。後は、VSCの恩恵を受ければ良い。
以下の様に書けるため、レビュアにとっても優しい。
cy.dataCy('submit').click();
/// ↑ 2021.09.24 追記 ↑ ///
話がずれましたが
先程のdata-cyなどを駆使してE2Eテストにおけるプロフィールのフォームを操作するのが良いでしょう。
3. ログアウトする
最後にログアウトします。ログアウト後はマイページを訪問しようとしても不可となりますね。
テストケースを増やした時
E2Eテストは、ケース毎に環境をクリアしますので、毎回行うような、例えばログインなど、においては初回だけで良いと思われます。その時は、E2Eテストにキャッシュさせて、2回目からはセッションを読み取ってログイン済み状態にした上でテストを実行してくれるようにすると良いでしょう。
こちらもやり方は簡単なのですが、段々書いてる内容が多くなってきてしまったのと若干テーマとずれてしまうため、別記事にまとめたいと思います。
最後に
以上になります。ほんの少し、1ミリでも参考になれば幸いです。
この記事は個人が趣味の範囲で書いているため、間違っている内容があるかもしれません。其の場合、ご親切な方がいましたら教えていただけましたらありがたいです。
Discussion