KASHIMURA Blog

Webサービス開発のこと、個人的なことを書いているブログ

Cypressを使ってE2Eテストの導入をやったので振り返り

はじめに

最近、Cypressの導入をやったのでざっくりと振り返り。
導入環境は、React, Rails, GraphQL, Github Action を使ってるSaaS

なぜCypressを導入しようと思ったのか?

端折って簡単に書くと、お客さんにプロダクトの説明する時に正しく動いてないと大きな機会損失になる機能が何個かあって、これまでは、手作業でQAをしていたけど、確認するプラットフォームがWebとiOSAndroidの3つあるため、毎回とても手間で大変だということで、E2Eテストで動作の担保をしようと思い立ったのが導入の経緯。もちろん、保守工数の増加する点やCIが重くなるなど色々デメリットになりそうなことを天秤に掛けて考えてみたけども導入するメリットの方が上回ると判断した。

テスト環境の選定

手軽に始めたかったのでテストを動かす環境としては、すでに存在するステージング環境を使うようにした。いろいろネットの記事を読んでいると、CI上にテスト環境構築したり、E2E用のサーバー建てたりする案もあるみたいだったけど、あまり工数をかけずに始めたかったので、当分はステージングを使いながら様子を見て改善していく方針にした。

実行タイミング

ステージング環境にリリースされたあと、CIで自動実行するようにした。
なんでこのタイミングかというと、単純にテスト環境としてステージングを使っているので、ステージング環境へリリースされた後しかテストを実行できないから。

テストデータ作成の工夫

テストデータの作成は、それ用のAPIを用意した。テスト実行前にこのテストデータ作成APIを叩くようにしてる。

E2Eテストは、Webだけじゃなくモバイル側にも導入する予定なのでテストデータの作成を全プラットフォームで統一しておきたかったというのが理由。

最初は、Railsを使っているのでcypress-on-railsというCypressからRailsのコードを実行してテストデータを作成できるライブラリを採用するのもアリかと考えたけれども、これだとモバイル側では別途テストデータを作成するものが必要になるのでやめた。

ちなみに、このテストデータを作成するAPIは、テスト環境でしか動かないようにしてるので本番への影響はないようにしてる。

テストデータ削除の工夫

テストデータの削除は、バッチ処理を使って定期実行するようにした。だいたい、クリーンアップ処理はテスト前に実行するのが定石のようだけど、以下のような理由で難しかったのでバッチ処理に切り出すことにした。

  • テストデータをテナントごとまるっと作成しているため削除対象テナントを判断するユニークな情報を持ち回る必要があるけど難しい
  • CIでテストを実行するのでテストが並列で動いている可能性があり単純にどのテナントを削除していいのか判断できない

ちなみに、テスト終了後にクリーンアップ処理をしないのは、その処理の実行が保証されてないから。たとえば、テストが落ちた時とかは実行されないのでゴミデータが残ってしまう。詳しくは、このCypressのベストプラクティスにも書かれてる。

ディレクトリ構成

TypesScript Deep Driveを参考に、ルート直下にcypressディレクトリを作成してプロジェクトを入れ子にした。

.
├── cypress
│   ├── package.json
├── src
│   ├── 各コード
├── Gemfile
├── package.json

既存のプロジェクトに依存させてしまうと、Jestやテスト以外のコードと型が競合してしまうので分けるしかなかった。

一応、Cypressでグローバル変数を使わないようにする手段としてlocal-cypressというライブラリがあったけど、プロジェクトを入れ子にして解決するならばライブラリに頼らなくてもいいと思い使わなかった。

この構成にして思ったけど、もう一階層上にディレクトリ作って、そこにCypressプロジェクトと既存のプロジェクトを置いた方がよかったのでは?と今になって思ってる。プロジェクトを入れ子にしているので、Cypressプロジェクトで依存関係がインストールできてないと、親となる既存プロジェクトを動かしたときエラーになるし、Cypressを使わないときもCypressプロジェクトを気にしないといけないのがなんだかなと思ってる。

セレクタ命名規則

Cypressから操作する要素には、セレクタとしてdata-cyをつけるようにした。命名規則は「機能名-項目名-要素名」としてる。たとえば、hogeForm-fullName-inputのような感じ。一意になればなんでもよかったので個人的に一番分かりやすいこの形を採用した。

さいごに

Cypressは、ドキュメントが充実してるのでとても助かる!
いろいろなケースでサンプルコードが用意されているので、迷ったらサンプルを真似できるのもよかった。

実際に運用を何ヶ月かしてみて感じたことは後日で追記予定

参考