こんにちは。メルペイのフロントエンドエンジニアの @tokuda109 です。Merpay Tech Openness Month 2021 の13日目を担当します。
メルペイのフロントエンドチームは、管理している全てのサービスに対し E2E テストを継続的に実行しています。E2E テストの導入に関する取り組みについては「Cypress + TestRail による Frontend E2E テストの効率化について」で詳しく書かれています。
全てのサービスで E2E テストが導入されていますが、この記事で述べられているとおり、安定して動作しているわけではありません。テストが失敗することが多々発生していました。
本記事では、E2E テストがなぜ安定して動作しないかを調査し、どのように改善したかを紹介します。
背景
メルペイのフロントエンドチームは、テスト、パフォーマンス、アクセシビリティ、セキュリティをプロダクトの重要品質と定め、継続的に計測する仕組みを作っています。
E2E テストは重要指標の中のテストに属し、日々開発を行う中で予想外の影響が発生していないかを確認するために、継続的に実行されるようにしています。
しかし、E2E テストが常にパスするわけではなく、失敗することが多々ありました。
テスト実行が失敗する原因は多種多様で、簡単に解決できないものも多いため、まとまった作業時間を確保し、問題解決に取り組みました。
原因の全量洗い出し
まず最初は、全てのサービスで失敗の原因となっているものを洗い出し、全量を出すことにしました。
原因がサービス固有のものなのか、原因がどれくらいの種類あるのかを把握するために、次の項目をスプレッドシートに整理しました。
- エラー内容 (エラーメッセージ)
- エラー種別
エラー種別は次の図の背景色が付与されているカラムで、いくつかのパターンに分類することができました。
作業計画
原因の全量が把握できたので、次は目標の設定をしました。一度に全ての問題を解消することは不可能なため、次のような基準を設定し、作業に着手する前に優先度を決めました。
- 複数のサービスで発生している問題は解決する
- 解決しないとリグレッションテストが動作しないものは解決する
- 解決するには多くの時間が必要となるが、後述する Cypress のリトライ機能を使うと成功することがあるものは一旦リトライ機能を使う
この基準を元に、最初のスコープで解決を目指す問題と、次のスコープで解決する問題を分けました。
問題点とその対策
ここからは問題の原因と解決方法を一つずつ紹介します。
ネットワーク設定の不具合
メルペイでは、CircleCI 上で E2E テストをテスト環境に向けて実行しています。外部環境からテスト環境にアクセスすることはできないため、E2E テストを実行する時だけ一時的にアクセスを許可する仕組みがあります。
E2E テスト実行時にアクセス許可を登録する処理が、全てのサービスで度々失敗していました。
原因としては、テストを実行するアカウントのアクティベートとアクセス許可の登録が同時に行なわれていたからでした。アクティベートの方が早く終われば問題なく動作するが、逆の場合はまだアクティベートできていないため、登録する処理が失敗していました。
アクセス許可を登録する処理をリトライするように修正し、今は安定して動作しています。
テストデータ作成の不具合
E2E テストでは、テスト用に作成したお客さまデータを使ってテストを実行する場合があります。
テスト用のお客さまデータの作成には、user-tkool というツールを使っていて、このデータを作成する時に度々失敗していました。
Customer Pool というお客さまデータ作成時に作成済のものがあれば即座にそれを返す機能が user-tkool に追加されたため、この機能を使う方法に移行しました。
仕組みを簡単に説明すると、特定のシナリオに沿ったお客さまデータをuser-tkoolに要求すると、シナリオ通りの手順でデータを作って返してくれます。シナリオは何回も使い回されるはずですので、裏で新規データを作成しておき、繰り返し実行したときは高速に作成済みのデータを返してくれます。
同じシナリオであれば、次からは確保されているデータを返却するため、データの取得の安定性は抜群に上がりました。
Customer Pool に移行する前は、テストを実行する度にデータを一から処理を行い、作成していました。Customer Pool 移行後は裏で準備されたデータを返却するだけなので、高速で E2E テストが失敗することも基本的にはありません。
3rd party ライブラリの不具合
特定のサービスで、「権限がありません」という文言が画面に表示され、テストが失敗していることが多々ありました。
この問題は調査に時間がかかるため、最初の作業スコープでは Cypress のテストリトライ機能を使って一時的に回避しました。(何度か実行するとテストが正常に動作するため)
失敗原因
次の作業スコープで原因調査に取り組みました。原因としては、Store 層で使っているライブラリに原因があることが分かりました。
メルペイは Nuxt.js をアプリケーションフレームワークとして採用していて、ページリクエストを受け取った時に HTML レスポンスを返す処理が行われます。
処理の順番として簡易に説明すると、リクエストを受け取り、ミドルウェアが処理された後、返却する HTML を組み立てるために各コンポーネントが評価されていきます。この時にミドルウェアで Store 層に追加したデータがコンポーネントから参照した時に失われている場合がありました。
保持しているはずの権限データが参照できないため、権限がありませんという文言が表示されていました。説明用のコードとしては次のとおりです。
// src/middleware/role.ts
export default async function () {
await ctx.actions.fetchRole(); // fetch account's role
console.log(ctx.getters.hasRole); // -> true
}
// src/containers/page/index.vue
const mapper = createMapper(store);
export default Vue.extend({
computed: {
...mapper.mapGetters(['hasRole']),
hasPermission(): boolean {
console.log(this.hasRole); // undefined
return this.hasRole;
}
}
});
このライブラリは、問題が発生しているこのサービスでしか使っていないため、他のサービスと同じ実装にしてみて数日間 E2E テストの結果を見てみると、発生しませんでした。
担当チームに次のような選択肢があるが、どのように対応するべきか確認しました。
- 他のサービスと同様の実装に移行する
- 引き続きリトライ機能を使ってテストしていく
- 使っているライブラリを修正する
この現象が発生しているのはテスト時のみということで、今は一旦リトライ機能を使うことになりました。今回の調査では、使っているライブラリに原因があるというところまでしか把握できていないため、別の機会で根本原因を行いたいと考えています。
関連するマイクロサービスの不具合
全てのサービスで、500 Internal Server Error は度々発生しています。
エラーが発生する原因はテスト環境固有の理由で発生することが多く、たくさんのマイクロサービスが協調して動作しているため、原因の究明はとても難しいです。
この問題の解決としては、権限がありませんエラーと同様に、Cypress のテストリトライ機能を使って、リトライすることにしました。テストを実行した瞬間に偶然発生したエラーの場合は、リトライすることで次のテストでは成功することが期待できます。
ヒューマンエラー
最後は人が起因となって発生するエラーについて紹介します。
人間が何かしらの失敗原因を作ってしまい、テストが失敗します。失敗の原因は多種多様で、次のようなものがありました。
- 機能開発が活発なため、テスト環境に反映されているアプリケーションとテストが期待する挙動にズレが発生してしまう
- テスト用のデータに設定した日付を超えたため、上手く動作しなくなる
- データを更新しながらテストしていく時に失敗すると更新された状態のままになる (手動で元に戻す必要がある)
解決方法としては、CircleCI のテストジョブが失敗した時に Slack に通知が届き、気づくことができるようにしました。気づかずにテストが失敗し続けることを防ぎ、各チームが自主的に保守していく意識が芽生えたように感じます。
最後に
本記事では E2E テストが失敗する原因をどのように改善したかについて紹介しました。
フロントエンド側で可能な対処方法を用い、テストが成功することを目標にして作業をしました。今回はリトライすることで解決した問題が多かったですが、根本的な原因の究明を引き続き行っていきたいと思います。
現状把握の方法、計画的に取り組むアプローチ、各問題に対する判断等、泥臭い部分ではありますが、本記事を読んだ方に何かしらお役に立てれば幸いです。
次回 Merpay Tech Openness Month 2021 執筆担当は @hmj さんです。引き続きお楽しみください。