📆

mabl Plan設計をマスターする

2022/12/24に公開

イントロダクション

こんにちは。sumirenといいます。
CIerでテックリードをやったり、スタートアップでフリーランスエンジニアをやっています。

CIerにおけるテックリード業務で開発プロセスのモダナイゼーションを担当しており、先進的な品質エンジニアリングの取り組みとしてmablを利用しています。

よろしくお願いいたします!

背景

mablは最先端のローコード品質エンジニアリングプラットフォームです。
最も主要な機能は、WebのUIテストです。

先日、mablのコア機能について全体像を体系的に解説しました。

上記の記事でも触れたとおり、mablにおける品質エンジニアリング上の工夫のほとんどは、Planの構成や設定に現れると思っています。

この記事では、筆者のPlan設計のプラクティスをシェアします。

この記事のゴール

この記事のゴールは、既にmablについて経験がある方が、これからPlanを使ったりPlanの使い方を改善するうえで、学びを提供することです。

そのため、この記事では、初心者向けの概要説明などは行いません。
初心者の方向けには、こちらの記事が少しでも役に立てば幸いです。

この記事の流れ

この記事では、Plan設計のプラクティスを、実際の設計の流れに沿って解説します。
実際の作業順序に沿うことで、プラクティスを適用するイメージが持ちやすくなると考えたためです。

また、最後におまけとして、Planの実際の設定におけるTipsを紹介します。

ナレッジを詰め込んだ結果、かなり長い記事となってしまいました。
画面右下の目次を活用いただき、興味があるところから読んでいただければと思います。

1. Planの構成を決める

例えば、以下のような戦略を実現したいとします。

  • コアな振る舞いのテスト
    • Dev環境デプロイ後に実行、Stg環境にもデプロイ後に実行
  • あまり落ちないエッジケースのテスト
    • Stg環境にデプロイ後に実行

このとき、どのようにPlanを構成するか、早い段階で決めておくことが重要です。

1-1. Planの構成パターンを知る

シンプルなPlanの構成は2つ考えられます。

左のパターンは環境ごとにPlanを1つ作成する考え方で、右のパターンは観点ごとにPlanを1つ作成する考え方です。

Per-environmentパターンは、シンプルかつ簡単です。
環境の数だけPlanを作成し、実行したいテストをPlanに紐付けるだけです。

一方で、例えば、「Stg環境において、エッジケースのテストは1ブラウザでいいが、コアテストは複数ブラウザで実施したい」といった要求があると、この方法では対応できません。
対象ブラウザはPlanに指定するものであり、Planに紐付けられたテストごとには調整できないためです。

Per-perspectiveパターンでは、「Stg環境において、エッジケースのテストは1ブラウザでいいが、コアテストは複数ブラウザで実施したい」といった戦略も実装できます。Planをテスト観点ごとで分けているためです。
この方法では、先ほどと逆に、「同じ観点のテストを、Dev環境では1ブラウザだが、QA環境では複数ブラウザを対象にしたい」といったユースケースには対応できません。

1-2. Planパターンを早めに決める

これらのパターンは優劣ではなく、トレードオフです。本質的に、所属するプロダクトや組織において、テストの濃淡を観点でつける傾向にあるか、環境(=開発ライフサイクル上のステージ)で付ける傾向にあるかで判断すると良いと思います。

最も柔軟な方法は以下のように環境 × 観点単位でPlanを作成することです。便宜上、Atomic Planパターンとします。
以下の図はその例です。コアのテストケースが増えるたびにDevコアとStgコアを忘れずにメンテする必要があることが分かります。柔軟性を得た代わりに、メンテナンスコストがかかるということです。

どのような構成方法でも構わないのですが、重要なのは、構成方法を早い段階で明確にすることです。

個人的には、特に最終形が見えていなければ、最初はPer-environmentパターンでも良いかと思います。素直に分かりやすいためです。

2. テストの実行タイミングと実行方法を決める

Planの構成が決まったら、テストの実行タイミングと、タイミングごとの実行方法を定める必要があります。

説明しやすいように、先ほどの例を使いまわします。Per-perspectiveパターンで、コアテストのための環境横断のPlanがあると仮定します。

2-1. テストの実行タイミングを決める

まず、Planの実行タイミングを検討します。

これは一般的なプロセスで方針を考えるだけです。
例えば、以下のようなアウトプットになります。

  • PR作成時、CI環境で実行
  • PRマージ後、Dev環境でも実行
  • リリース前に、Stg環境でも実行

2-2. テストの実行方法を決める

実行タイミングが決まったら、それぞれのタイミングにおける実行方法を検討します。

前提知識として、mablでは、テストの実行方法は以下のようになります。
この表は、こちらの記事で解説しました。

例として、上記の3つのタイミングで実行方法を検討します。

  • PR作成時、CI環境で実行
  • PRマージ後、Dev環境でも実行
  • リリース前に、Stg環境でも実行

上記の表を見ながら、以降の解説を参照ください。

2-2-1. PR作成時の実行方法

上記表の一番下の行が示すように、CI環境における実行に対応できるのは、Ad hoc test runsだけです。
CI環境上で立ち上げたWebフロントエンドに対し、CI環境上でmabl CLIを用いてテストします。

Ad hoc test runsでは、Planを直接実行することはできませんが、mabl CLIを用いて、Planに含まれるすべてのTestをCI環境ローカルで実行することはできます。

mabl tests run --from-plan-id xxx

これはあくまで、Planに含まれるTestを1つずつ指定するのと同じ動きであり、正式なPlan実行とは全く異なります。例えば、複数ブラウザでの実行はできませんし、Planのshared variablesは機能しませんし、結果はmabl Cloudに保存されません。

また、高機能なPaaSを使っている場合は、PRごとにプレビュー環境が提供される場合もあるかと思います。
こうした場合であれば、プレビュー環境を対象にCI環境でmablを動作させたり(①)、正式なテストとしてmabl Cloudからプレビュー環境に対してテストを行う(②)こともできます。

mabl tests run --url xxx --from-plan-id xxx 
mabl deployments create --url xxx --application-id xxx --environment-id xxx --await-completion ...

また、後者のようなmabl Cloudにおけるテスト実行は、mablがCIツールとのインテグレーションを提供している場合もあります。
例えばGitHub Actionsの場合、mablはマーケットプレイスで「Run mabl tests」Actionを提供しています。
https://github.com/marketplace/actions/run-mabl-tests

2-2-2. PRマージ後、Dev環境に対する実行方法

Dev環境やStg環境は、通常クラウドやオンプレにデプロイされ、インターネットからアクセス可能な環境です。

したがって、CI環境からのTestレベルでのAd hoc test runsに加え、mabl Cloud上でのPlan実行が可能です。Plan実行では、Ad hoc test runs、Deployment、Timer / Scheduleなどの実行方法が選択できます。

整理すると、Dev環境やStg環境では、以下のような選択肢があることになります。

  1. Ad hoc test runsでTestを実行(CI環境と同様)
  2. Ad hoc test runsでPlanを実行
  3. DeploymentでPlanを実行
  4. Timer / ScheduleでPlanを実行

個人的には、3の方法がおすすめです。CICDパイプラインのデプロイに引っ掛けて、Deploymentを生成し、Planをトリガーします。

まず、1では、以下のようなmablの強みを活かせません。

  • Planがmabl Cloudで実行されることで、大量のTestを並行で実行できたり、複数ブラウザを対象にできる

2では上記のメリットは享受できますが、正式なテストとは扱われないため、以下のメリットが活かせません。

  • 自動修復やインサイト、メトリクスがmabl Cloud上に生成される

残った選択肢の中でDeploymentをおすすめする理由は、フィードバックのスピードです。

Timer / Scheduleは定期実行となるため、どうしてもデプロイ後バグを検知できるまでタイムラグが発生します。
Deploymentなら、CICDパイプラインに引っ掛けることで、デプロイ後すぐにフィードバックを得ることが出来ます。

また、先日の記事で説明したとおり、Deploymentの概念はとても直感的で、当該のApplication・Environmentに紐づく全てのPlanを簡単に実行することができます。

補足:プチプラクティス

おまけとして、ちょっとしたプラクティスを紹介します。

それは、Timer / Scheduleによる定期実行も併せて設定しておくことです。

mabl Cloud上での実行が少なすぎると、収集できる実行データが少なく、インサイトを活用することが難しくなります。例えば、以下のようなことです。

  • Visual Changesを検知するためのベースラインの構築が行われるのが遅くなる
  • テストのFlakinessに関するメトリクスが、週ごとにバラバラとなってしまい、トレンドが見えない

そのため、CICDパイプラインとの統合が難しいなどの理由で、正式なテストとしてPlanが実行される頻度が低いなら、Timer / Scheduleによる定期実行を併せて設定しておくことで、PDCAサイクルの高速化が期待できます。

2-2-3. リリース前、Stg環境に対する実行方法

Stg環境や本番環境も、技術的にはDev環境と同じ方針をとることができます。
しかし、開発プロセスの性質などに応じて、Dev環境とは取られる実行方法が変わるかもしれません。

例えば、1週間単位のイテレーション/リリースなど、シームレスな開発プロセスに比べてリリース頻度の低い組織では、リリースプロセスの一部にStg環境での手動実行を含める選択肢もあります。
Deploymentはmabl DesktopなどのGUIからも生成することができますので、何もCICDパイプラインとの統合は必須ではありません。

3. テストデータとうまく付き合う

一般に、アプリケーションとシナリオの性質によっては、裏からのデータ投入が必要となる場合があります。
例えばサロン予約アプリで、サロンを予約するシナリオなら、裏から「この店舗はこの日予約可能」といった設定を入れる必要があると思います。

ユニットテストであれば、テストデータのライフサイクルで困ることは少ないと思います。
テストケースごとにデータを独立して生成するといったことが簡単だからです。

しかし、特にUIテストをDev/Stg環境に対して実行する場合、過去データとどう付き合うかといった検討事項が発生します。

また、mabl固有のテストデータ生成の設定方法についても、効率や保守性に関してプラクティスを共有する価値があります。

3-1. テストデータ生成の落とし穴を避ける

Planにおいて生成されるテストデータのライフサイクルは、大きく分けて4つのパターンが考えられます(エンティティごとに異なるかもしれません)。

  1. Planで毎回テストデータを生成する
  2. Planで毎回テストデータを生成するが、既に存在すればそのデータを使う
  3. Planで毎回テストデータを生成し、削除する
  4. 環境構築のたびに手動実行するためのスクリプトを用意する(mablのコントロール外にする)

制約上1が問題ないなら、1で良いと思います。

問題は、毎回生成すると、実行時間やキャパシティに対して悪影響が大きかったり、システムの状態に不整合を発生させうるエンティティです。

マスタデータ系であれば、4が最も手軽です。CI上でも、ローカルで立てたDBに対してそのスクリプトを実行したり、ローカルでスタブサーバーを立てることも可能です。

一方で、例えばサロン予約アプリで、店舗データを4で投入した場合、「2022年12月24日は何時が予約が空いているか」といったデータはどのように投入すればよいでしょうか。

4ではできないことは明白ですが、加えて、1の方法も難しいかもしれません。店舗 × 日時の組み合わせに対して、タイムテーブルは1対1のはずであり、既にデータが存在すると、システムの状態が矛盾するためです。
したがって、このような場合は、2か3から選択する必要があるでしょう。

個人的には、こうしたケースでは強く2を推奨します。

3の方針は一見合理的に思えますが、以下のような懸念があります。

  • 同じ環境に対してPlanが同時に2重で実行されたときに、後続のPlan実行が落ちないか
    • 削除に甘えて、Plan開始時にテストデータが存在しないことが前提になっていないか
    • 後続のPlan実行中に手前のPlanが完了し、テストデータを削除してしまったことで後続のPlanが落ちないか
  • テストデータ削除がバグっていたときに、データが異常な状態となってしまい、Planがパスしなくならないか

もちろん、こうした懸念についてトリアージをしたうえで、2を選択することは問題ありません。
例えば、開発プロセスで2重実行が起きないようにコントロールするといった対策が考えられます。

3-2. Planでテストデータを生成する方法

mablのPlanをどのような設定することでテストデータを生成するかも、重要です。
次の図は、mablでPlanをつかってテストデータを生成する方法の選択肢です(例によって、命名は適当です)。

Setup stageパターンと名付けた左側の図は、テストデータ生成専用のTestを作成し、そのTestが終わってから本来のテストを実行する方法です。
Planには環境変数を共有する機能があるため、生成されたデータのIDなどは、Testをまたいで共有することができます。

一方、Self-sufficientパターンと名付けた右側の図は、1つ1つのTestが自分のデータを作成(自給自足)し、その後テストを行う方法です。
Testの中でデータの生成と実際のテストを行うため、テストをまたいで環境変数を共有する必要はありません。

個人的には、Self-sufficientパターンを強くおすすめします。
Setup stageパターンは、様々な懸念があります。

  • ローカルやCI環境におけるテストが難しくなる
    • Testをまたいで環境変数をシェアするShared variablesや、実行順序を制御するStageの機能は、Planのものである。ローカルやCI環境ではTestレベルでのAd hoc test runsが中心となるため、こうした機能に依存したTestを実行しようとすると、破綻する
  • テストを追加したり修正する度にテストデータ生成専用のTestに影響してしまい、コンフリクトが発生しやすくなる
  • 1つのテストデータを複数のTestで共有するような設計をしてしまうことにつながりやすい
    • 一見効率的に思えるが、Testの実行順序に依存することでPlanが不安定になったり、Testの改修影響が拡大することにつながる。
    • 例えば、サロン予約アプリの1店舗を2つのTestで共有したとすると、予約のTestが先に行われる前提でキャンセルのTestを組んでしまい、予約のTestの内容にキャンセルのTestが依存したり、たまたま逆順で実行されると予約が存在しなくて落ちたり、といったことが起きる。

3-3. コラム: 冪等性を好み、ステートを嫌う

私見ですが、冪等性やステートレスといった原則は、品質エンジニアリングにおいても活用できると考えています。
特に、私はテストデータ生成に関して設計するとき、より上位の方針として冪等性やステートレスを強く意識しています。

例えば、先ほど注意した「テストデータを生成した後に削除する」という設計は、

  • 開始時点でテストデータがある
  • 開始時点でテストデータがない

といったステートに加え、

  • 開始時点でテストデータはあるが、途中で消える

といった第3のステートを生み出してしまい、「ステートを嫌う」という原則に反します。

「テストデータをPlan内で削除しない」という方針では、ステートが2つに絞られる上、削除が発生しないことで、

  • 開始時点でテストデータがある→終了時点でテストデータがある
  • 開始時点でテストデータがない→終了時点でテストデータがある

といった形で、状態の遷移は必ず「テストデータがある」に収束するため、冪等です。

同様に、Setup stageパターンを推奨しないことの根底にも、「ステートを嫌う」があります。

Setup stageパターンでは、Plan全体というスコープの広いステートを持つことにつながります。
一方のSelf-sufficientパターンは、ステートは個別のTestに閉じるため、Planレベルではステートレスであり、より堅牢です。

これはプログラミングに喩えるとわかりやすいかもしれません。PlanはTestを並列実行しますので、マルチスレッドアプリケーションに喩えられます。Test1つ1つがモジュールだとすれば、Plan内のTest間で共有されるテストデータはグローバル変数です。

つまり、「PlanのテストデータをTest間で共有する」とは「マルチスレッドアプリケーションで、色々なモジュールからグローバル変数に書き込んだり読み込んだりする」ようなことです。心のざわつきを感じませんでしょうか。

4. 実行回数と実行時間をコントロールする

テストの実行タイミングと、タイミングごとの実行方法を検討する章では、例として以下のような実行タイミングを定めました。

  • PR作成時、CI環境で実行
  • PRマージ後、Dev環境でも実行
  • リリース前に、Stg環境でも実行

これらのタイミングごとに、どの程度のテストを行うか検討する必要があります。

基本的には、Shift-Leftの考え方で、より早いタイミングでより多くのテストをしたいです。

一方で、2点の考慮事項があり、バランスを取る必要があります。

  • 実行回数による費用
    • mablは月あたりの実行回数(Testの実行数)に応じた契約であり、実行回数が増えるとコストが増える
    • 例えばPR作成時にプレビュー環境で複数ブラウザテストをしたとしたら、コストが膨大になる
  • 実行にかかる時間
    • 例えばPR作成時のCI環境での実行時間が30分かかったとしたら、開発生産性が落ちる
      • レビュー指摘→対処のあと、30分待たなければ次のアクションができない

4-1. 実行回数の試算方法を知る

まず、前提知識として、実行回数の試算方法を説明します。

ケーススタディとして、あるチームのmablの構成や実際に開発ライフサイクルにおけるデプロイ頻度を仮定した上で、黒い吹き出しで実行回数を試算しました。

以下の図解で試算方法を確認してください。
※見づらくて恐縮ですが、右から左に見ていくと、条件→試算結果という順序になり見やすいかと思います。

いくつか補足します。

  • 開発ライフサイクルにおいて、当然Planがfailする場合などもあります。バグがあった場合や、テストが不安定な場合です。そのような場合にはテストを再実行する必要があるため、合計回数に一定の係数を掛けるとよいでしょう。例えば30%失敗するなら、300回程度の追加実行が発生するでしょう。
  • これは1アプリケーションの試算です。組織が3アプリケーションを抱えているなら、それぞれ同様の計算を行ったうえで合算します。

4-2. 実行回数と実行にかかる時間を削減する

そのうえで、実行回数や実行にかかる時間を削減するためには、以下のような選択肢があります。
当然ながら、こうした選択肢は、早期のバグ検出とトレードオフになるため、総合的な判断が必要です。

  1. Planに紐付いているTestを減らす
    • 例えば、Dev環境ではもっとテストを絞り、Stg環境のテストで担保できないか
    • 例えば、ほとんどfailしないテストはそもそも削除できないか
  2. 複数のTestを1つにマージする
    • 例えば、CRUDで4Testになっているなら、1Testにまとめられないか
      • やりすぎに注意。何が理由でテストが落ちたかわかりづらくなったり、メンテコストが高くなるため
  3. Planの対象ブラウザ数をへらす
    • 例えば、Stg環境ではChromeを省いてはどうか(Devでやっていることを根拠に)
  4. Planのトリガーを減らす
    • 例えば、Timer / Scheduleによる定期実行で十分Shift-Leftできていると感じるなら、Deploymentトリガーを削除してはどうか
    • 例えば、Devに紐付いているPlanを代わりにStgに紐付けることで、より実行頻度の下げられないか
    • 例えば、Deploymentの頻度が高いのであれば、逆にTimer / Scheduleを無効化してはどうか

おすすめは、CI環境やDev環境など、開発ライフサイクル上の早いステージにおける実行回数/実行時間を削減することです。

特に、CI環境でのテスト実行にかかる時間は、開発生産性にダイレクトに効いてきます。
「待っている間、他のことをすればいい」と感じるかもしれませんが、エンジニアからすると、1つのタスクに集中するためにどんどんPRマージやPRレビューをしたく、テストに待たされることで生産性に大きな影響が出ます。

また、Dev環境でのテスト実行はStgや本番に比べて多くなるため、デプロイあたりの実行回数を減らすことで、全体の実行回数を大きく削減できます。
特に、ブラウザパターンは最も手軽に削減できる項目です。

おまけ: Plan作成時のTips

Planを実際に設定する際のおすすめ設定をシェアします。

「設定しないと動かない」といったものは皆さん気づけると思いますので、「設定しなくても動くけど、知っておくと得」ものを中心に紹介します。

わかりやすいように、UIベースで紹介します。

おまけ1. Browsers

ブラウザは、特にこだわりがなければChromeかEdgeがおすすめです。

ChromeかEdgeで実行すると、mablのUnitifed Runnerという機能が自動的に有効化されます。
Unitified RunnerにはIntelligent waitという機能が付随しており、動作の早い画面/遅い画面を学習します。

これにより、以下のようなメリットがあります。

  • 早い画面は素早く操作するため、実行スピードが向上する
  • 遅い画面は長く待機するため、テストが失敗しづらくなる

おまけ2. Page Interaction Speed

テストが不安定な場合、Page Interaction Speedを低めに設定しておくと良いです。
描画速度起因でのテスト失敗を軽減することができます。

Planごとに共通化したい場合、EnvironmentにPage Interaction Speedを設定し、PlanではInherit from environmentを選択することで、実行対象のEnvironmentから引き継ぐことも可能です。

おまけ3. Visual change learning

Visual change learningをオンにしておくことをおすすめします。

mablにはVisual Regression検知に活用できる、Visual change detectionという機能があります。
デフォルトでは、環境ごとにPlanの実行結果を記憶しておき、スクショの差分を検知してPlanの実行結果上に表示するという機能で、常時有効化されています。

Visual change learningをオンにしておくことで、より賢い挙動になります。

  • AIが画面について学習し、スクショのうち検知不要な部分は検知しない
    • 動的なデータ部分や広告部分など
  • 検知した差分(Visual change)を、まとめてダッシュボードで確認できるようになる

詳細は公式Docを確認ください。

おまけ4. Automatically retry on failure

Automatically retry on failureと、Retry failed tests onlyを両方オンにしておくことをおすすめします。

mablでPlanを運用していると、画面の描画速度などに起因して、テストが不安定(Flaky)になることがよくあります(これはUIテスト全般に言えます)。

mablのPlanは1つでもTestが落ちたら失敗とみなされますが、この機能により、Planのリトライが可能です。

  • Automatically retry on failureのみオンにすると、1つ以上Testが失敗したら、Plan全体をリトライします。
  • 加えて、Retry failed tests onlyもオンにしておくと、1つ以上Testが失敗したら、落ちたテストに限ってリトライします。

基本的には後者のほうがおすすめです。まず、Flakinessによって、リトライ時に別のテストが失敗してしまうという懸念がなくなります。加えて、リトライによる費用と実行時間へのインパクトも最小限にできます。

まとめ

まず、Planを構成するうえで、チームで使うPlanパターンを決めます。
特に最終形が見えていなければ、最初はPer-environmentパターンがおすすめです。
簡単で分かりやすいためです。

次に、「PR作成時、CI環境で実行」「PRマージ後、Dev環境でも実行」... といった形で、テストの実行タイミングを洗い出し、それぞれmablのどの実行方法を適用するか決めます。
CI環境ではAd hoc test runsを使い、Dev環境では、CDパイプラインでDeploymentをトリガーすることがおすすめです。
また、Planの正式な実行頻度が少ないようであれば、Timer / Scheduleを併せて活用することもおすすめです。

テストデータをmablで生成する場合、テストの安定性を高めるため、テストデータの削除に依存しないことをおすすめします。
mablのPlanに対する設定方法は、2022年12月時点ではSelf-sufficientパターンをおすすめします。CI環境やローカルでも実行可能とするため、Plan固有の機能に頼りすぎないほうが良いためです。
テストデータ生成では総じて、ステートレスと冪等性を意識することをおすすめします。

費用効率や開発生産性を改善するために、テストの実行回数と実行時間を削減することも重要です。
CI環境やDev環境など、開発ライフサイクル上の早いステージで、優先度の低いユースケースや観点を削減することがおすすめです。
ボリュームゾーンであり、またエンジニアのCI待ち時間を減らすことにもつながるためです。

この記事を読んで、mablのPlan設計をマスターいただけましたでしょうか。
mablやPlanについて、少しでも学びがありましたら幸いです。

追伸

mablや品質エンジニアリングは、これからどんどん盛り上がる領域だと感じています。

この記事では、mablがもっと盛り上がると期待を込めて(!)、実践的なプラクティスを詰め込んでみました。

よければTwitterもフォローお願いします!
@sumiren_t

Discussion