🤖

E2E 自動テストの布教に立ち塞がる5つの壁と打ち込んだ楔

2023/12/19に公開

この記事は、Magic Moment Advent Calendar 2023 19日目の記事です。

こんにちは、 Magic Moment の一人だけ QAE の yano です。

一人だけの QAE が GUI を用いた E2E 自動テスト(以降、自動テストと表記)を書いて運用していくことは、自動テストの新規作成やメンテナンスを行うには限界がありますし、他の QA 活動が進まなくなるという問題が出てきてしまいます。

そこで今回は QAE ではなく開発メンバが主体となって自動テストの運用をできるように仕組みを整える必要がありました。

本記事では自動テストを開発メンバに布教していく際に感じた5つの壁と、壁に対して打ち込んできた楔について書かせていただこうと思います。

1の壁 : 何のための自動テストか分からない

ユニットテストはあるけど自動テストはないという現場ですと、漠然と自動テストもあった方が良いとは思っているものの、何のために苦労して自動テストを整備したら良いかが分からないという壁にぶつかりました。

そこでまずは、自動テストで気軽に OSS やインフラのバージョンアップできるようにするという開発メンバに対するメリットを提示し、それを自動テストの目的としました。

理由としては、現場では OSS やインフラの軽微なバージョンアップでもある程度気合いを入れて動作確認をする必要があり、アップデートが高頻度にできていないという課題があったためです。

昨今の開発現場ではよく言われる話ですが、何もしていないのに壊れるのではなく、何もしていないから壊れるという事実を受け止め向き合っていく必要があり、それに対する仕組み作りがモダンな開発現場では求められます。

2の壁 : どのような自動テストを作成したら良いか分からない

自動テストの目的を定めたは良いものの、目的だけでテストコードを書くことは難しいという壁にぶつかりました。

具体的にどのような自動テストを書けばいいのか分からないままでは進まないので、テストコードを実装する前に自動テスト用のテストシナリオを用意しました。

プロダクトにおけるクリティカルユーザージャーニー (CUJ) は何かをヒアリングし、自動化するテストシナリオを定め、そのテストシナリオを Notion 上に日本語で書いて管理しました。

Arrange-Act-Assert (AAA) パターンに沿って、該当の自動テストが動作するための前提条件、テストをする上での操作、そしてアサーションを各テストシナリオで記述し、テストコードを実装するとき迷わないような形にしています。

3の壁 : 自動テストのデザインパターンが分からない

プロダクト開発のメンバは当然プロダクトコードにおけるデザインパターンは熟知していますが、自動テストコードのデザインパターンは聞いたことがないという方が多くいるという壁にぶつかりました。

自分が入社する前から Cypress でいくつかのテストコードは実装されていましたが、案の定リニアスクリプトになっていました。

フィジビリスタディの段階ではリニアスクリプトで問題ないですが、ある程度本腰を入れて運用していく場合はテストコードの構造化と抽象化を行う必要が出てきます。

リニアスクリプトでテストコードが次々と追加された場合、SUT (System Under Test) の変更や Flaky に耐えられず、しばらくすると誰もメンテナンスできない立派なシェルフウェアが出来上がるためです。

そこでメンテナンス性と可読性の高いテストコードが実装できるように SUT に沿ったデザインパターンとディレクトリ構成を決定し、どこにテストシナリオとテストの操作を書くか迷わないようにするための実装ガイドラインを策定しました。

例えばログインをするテストを書く場合は次のように記述します。

// テストシナリオ側
// cypress/e2e/smoke.cy.ts

describe('Smoke Test Suite', () => {
  it('ログインできる', () => {
    // テストシナリオは Commands をメソッドチェーンしていくことで記述可能
    cy.ログイン(Cypress.env('username'), Cypress.env('password'));

    // Assert したい内容を記述
    cy.contains('ログイン後の画面です。').should('be.visible');
  })
});
// ログインアクション側
// cypress/support/commands/auth/login.ts

declare namespace Cypress {
  interface Chainable<Subject> {
    // アクションを書くファイルの先頭で型定義
    ログイン(email: string, password: string): Chainable<Subject>;
    }
}

Cypress.Commands.add('ログイン', (email: string, password: string) => {
  // 実行する操作を記述
  cy.visit('/auth/login');
  cy.get('input[type="email"]').type(email);
  cy.get('input[type="password"]').type(password);
  cy.get('button').contains('ログイン').click();
});

4の壁 : 自動テストコードをどう書けば良いか分からない

実装ガイドラインを策定しても実際のコードがないところに Cypress のコードを開発メンバが書くことは容易でなく、日々の開発タスクもあるのでキャッチアップもなかなか進まないという壁にぶつかりました。

そこで、自動テストに対するモチベーションが高めの有志数名で定期的にもくもく会を開き、実装したテストコードはお互いにコードレビューすることでナレッジシェアをしつつ、テストコードがある程度整備されている状態にしました。

その後、フロントエンドアーキテクチャ会で開発メンバにドライバーを担当いただき、モブプロで自動テストを実際に書くという会を実施しました。

モブプロでは自動テストの実装ガイドラインに沿って、実際の自動テストをどのように書くのかをレクチャーすることで、開発メンバだけでもテストコードをメンテナンスし、追加できるようにスキルトランスファーを行いました。

フロントエンドアーキテクチャ会に関しては、こちらの記事で紹介されています。

5の壁 : 自動テストをどのように運用するのか分からない

自動テストがある現場の経験がない人にとっては、どのように自動テストを運用すれば良いのか想像がつかないという壁にぶつかりました。

まずはテスト結果が分かりやすい場所にわかりやすくフィードバックされる必要があります。

cypress-io/github-action を使用することで、テストの結果が Cypress Cloud 上に自動的に蓄積され、テスト結果のサマリを Slack へ通知まで行える状態にしました。

e2e-test-1.png

更に自動テストが Fail した時にいかにストレスなくデバッグができるかが鍵となります。

Cypress Cloud ではテスト結果のレポートがわかりやすい形で提供されますし、バージョン13から追加された Test Replay 機能が非常に強力で、テストが Fail したときに何が起きたかを Cypress Cloud 上で把握することに役立ちます。

e2e-test-2.png

そして自動テストのメンテナンスガイドラインを策定することで、自動テストを追加する時の新規作成フローと、自動テストが失敗した時にどのように対応するかという修正対応フローを明確にしました。

明日のアドベントカレンダーtnegishさん の「完全に理解した、TooltipとPopoverの違い」です。

最後に

弊社 Magic Moment では、 QAE はもちろんのこと、フロントエンド・バックエンドにかかわらず全方位的にエンジニアを募集中です! Magic Moment に少しでも興味を持っていただけたら是非エントリーください!

Discussion