UIテストをしながらマークアップ・レビューする、テスト画面上開発を考える


アプリケーション開発にはテストがつきものです。バックエンド、フロントエンドに関わらず、アプリケーションの品質・価値を担保するために、わたしたちはテストを書きます。

Gaji-Laboのスタートアップ支援においても、UIテストはプロダクトの品質を高め、価値を届けるために大きな役割を果たしています。

フロント開発においては主にUIテストを書きながら実装していきますが、UIテストはロジックだけに限らず、マークアップやスタイリングにおいても役に立つプロセスです。この記事では、コンポーネントの組み込みやスタイル調整をUIテストと共に進行する開発体験についてお話していきます。

なお、ここでは「テストファースト」や「テスト駆動開発」のような深い話はしません。もっと浅く身近なレイヤーの話をします。

通常のマークアッププロセス

マークアップやスタイリングは、ほとんどの方はChromeなどのブラウザでアプリケーションを開き、画面の描画と開発者ツールを駆使して表示確認をしていると思います。レビュワー側も提示されたURL等をブラウザで開いて確認をします。

通常このやり方で困ることはあまりありませんが、時に非常に確認しづらい状況があります。特定の状態・特定のケースでしか表示されないUIの確認です。

例えばこのようなケースがあります。

  1. ホバーで表示されるツールチップのスタイリング
  2. 不正な値を入力した際に表示されるエラーメッセージの組み込み
  3. APIから特定の値が渡された場合の表示の確認

ブラウザでこれらの描画をチェックするためには、手作業で状態を再現しなければなりません。

1. ホバーで表示されるツールチップのスタイリング

例えばあるアイコンをマウスオーバーすると表示するツールチップのようなUIを作ります。デザイン指示通りにスタイルが当たっているかを確認するため、ブラウザのデベロッパーツールでそのツールチップを選択しようとします。

ところが、マウスをアイコンから離すとツールチップは消えてしまいます。どうしたらよいでしょう?

2. 不正な値を入力した際に表示されるエラーメッセージの組み込み

例えばテキストフィールドに不正なメールアドレスが入力された場合に、エラーメッセージを表示したいケースです。それを表示させるためには、実際にメールアドレスではない文字列を入力しなければなりません。

コードを編集してスタイルを変更するとホットリロードで自動で再読み込みがかかるか、または自分でリロードをして確認をするのですが、その時にはまた不正なメールアドレスの入力からはじめなければならないのが面倒です。

3. APIから特定の値が渡された場合の表示の確認

例えば、APIから返されたメッセージを元にトーストUIを描画したい場合は、ローカルのモックサーバーから特定の値を返却させるためにコードを書き換えるか、あるいはローカルプロキシを使うという方法も考えられますが、いずれにしてもレビュワーにその方法を伝えなければレビューができないため、どうしてもコストがかかります。

フロントのコードを一時的に編集する方法

これらのケースは、フロントのロジックコードを一時的に編集する事で状態を再現できます。初期状態でそのUIが表示される様に書き換えたり、APIから値を受け取らずにコード上でインターセプトする、などです。

しかし、その一時的な編集内容はコミットしてはならないものであり、必ず原状回復しなければなりません。つまり結局はレビュワーにその方法を共有する必要があるのです。そして、うっかり戻し忘れてコミットしてしまうというリスクもあります。

テスト画面でマークアップをする・レビューをする

前置きが長くなりましたが、テスト画面を活用することで、上に挙げたような面倒な状態の再現が簡単になります。

ここで言う「テスト画面」というのはUIテストフレームワークに付属しているGUIのテストランナーのことで、実際にブラウザ上でUIテストを走らせる事ができます。例えば Cypress では cypress openPlaywright ならば playwright --ui のようなコマンドで起動します。

デモ画面

デモ用にこのようなシンプルな登録フォームを用意しました。以下のような挙動が実装されています。

  1. ヘルプアイコンをマウスオーバーするとツールチップが表示される。
  2. Email のテキストフィールドに正しくないメールアドレス文字列を入力するとエラーメッセージが表示される。
  3. Email と Password を入力して Register をクリックするとAPIにリクエストを飛ばす。この時、バックエンド側でエラーが発生した場合にはAPIからエラーメッセージが返され、フォーム上部にトーストとして表示される。

それぞれの状態を、今回はUIテストフレームワークの Cypress を使って再現してみましょう。

Cypress App で状態を再現する

Cypressのセットアップについては省かせていただき、以下のようなテストコードを書きました。

describe("Form Example", () => {
  beforeEach(() => {
    cy.intercept("POST", "/api/register", {
      statusCode: 422,
      body: { message: "このメールアドレスは既に登録されています" },
    });
    cy.visit("/form");
  });

  // 1. ヘルプアイコンをマウスオーバーするとツールチップが表示される。
  it("登録フォーム: ツールチップ", () => {
    cy.get("[data-cy=help-icon-mail]").trigger("mouseover");
    cy.contains("メールアドレスを入力してください");
    cy.wait(500);
  });

  // 2. Email のテキストフィールドに正しくないメールアドレス文字列を入力するとエラーメッセージが表示される。
  it("登録フォーム: バリデーションエラー", () => {
    cy.get("input[name='email']").type("INVALID_EMAIL");
    cy.contains("正しいメールアドレスを入力してください");
  });

  // 3. Email と Password を入力して Register をクリックするとAPIにリクエストを飛ばす。
  //    この時、バックエンド側でエラーが発生した場合にはAPIからエラーメッセージが返され、
  //    フォーム上部にトーストとして表示される。
  it("登録フォーム: APIエラー", () => {
    cy.contains("button", "Register").should("be.disabled");
    cy.get("input[name='email']").clear().type("mail@example.com");
    cy.get("input[name='password']").type("P@ssw0rd");
    cy.contains("button", "Register").should("be.enabled").click();

    cy.contains("このメールアドレスは既に登録されています");
  });
});

このテストをCypress Appで走らせます。

$ cypress open

見たいSpecを選んで実行すれば画面上で自動テストが実行されます。マウスオーバーや入力、クリックなどのユーザーアクションが忙しなく自動処理され、画面は目まぐるしく移り変わります。

スナップショットを見る

テストが完了すると、左ペインのSpecリストから、任意のタイミングのスナップショットを選んで表示することができます。

例えばツールチップが描画された直後を選択してみましょう。

選択すると Pinned となり、その時点のスナップショットが表示されます。スナップショットは完全に静止していて、クリックやマウスオーバー、フォーカスなどには全く反応しません。つまり、思う存分ツールチップを観察することができるのです。もちろん、デベロッパーツールで詳しくスタイルを確認することもできます。

同じ様に、バリデーションエラーも見てみます。

そして、APIエラーによるトースト表示がこちらです。

この様に、面倒だった状態の再現がボタンひとつで簡単にできてしまいました。これならばレビューも効率的に進みますね。

メリットとデメリット

この手法の一番のメリットは再現が容易になって確認が簡単になることです。これはコードを書くメンバーよりも、レビューをするメンバーにとっての恩恵となるでしょう。また、従来ロジックを書く人間が主として触れていたUIテストに対して、より多くのメンバーが関心を持つことができることも大きな利点となるはずです。

もちろん、デメリットもあります。

まず、通常のブラウザに比べて環境が重たい上、テストコードが長くなればテスト完了まで待つ時間も延びます。そして細かな挙動・状態までテストコードに起こしているとテストコードが肥大化し、例えばCIなどでチェックを走らせている場合ビルド完了まで非常に長い時間を待たされることになるでしょう。

様々なテストの中で、UIテストは特にコストが高いです。いたずらにテストコードを増やさず、粒度を考えて計画的に運用したいですね。

おわりに

この記事では、テスト画面を開発環境として、そして作業者とレビュワーとの間をつなぐコミュニケーションツールとして活用する考え方について紹介してきました。

レビュー依頼された時に、実際にレビューできる状態になるまでの準備には、チケット内容の把握も含めて意外に時間がかかります。出来るだけスムーズにレビューが進められるように効率化を図りたいです。

必要なポイントにしっかりリソースを費やせる環境をつくり、より高い価値の提供に注力出来るように考えていきましょう。

Gaji-Labo フロントエンドエンジニア向けご案内資料


投稿者 Oikawa Hisashi

フロントエンドエンジニア。モダンなJavaScript開発に関心があります。 デザインからバックエンドまで網羅的にこなすマルチデザイナーとして長く活動してきた経験を活かして、これから関わる様々なものをデザインしていきたいです。チームもコミュニケーションもデザインするもの。ライフワークはピアノと水泳。