こんにちは、九州大学大学院1年の@masaと申します。
私は、2024年11月から12月末までの2ヶ月間、メルカリ ハロのフロントエンドエンジニアとして、インターンに参加しました。
今回は、その中で特に注力したインテグレーションテスト戦略と、メルカリでの学びについてお話しします。
なぜ「メルカリ ハロ」のインターンに参加したのか
インターンに参加した主な目的は、大規模サービス、特にtoC向けのサービス開発を体験することでした。メルカリのサービスの中でもメルカリ ハロは、リリースして1年も経っていない新規のプロダクトであり、スピードと品質が求められる現場で、実践的な開発プロセスを学ぶ絶好の機会だと考えました。
また、メルカリという会社の雰囲気やカルチャーを直接体験して解像度を上げてみたいという興味も、参加の大きな動機の一つでした。
インテグレーションテストへの取り組み
インターン期間中、大小様々なタスクに取り組みましたが、中でも注力したのが事業者向け画面のインテグレーションテストです。私がジョインした時点で、テックリードの@ryotahさん主導で技術選定と環境構築は完了しており、テストカバレッジ向上に取り組む段階でした。
メルカリ ハロでは、インテグレーションテストに関して、メルペイのフロントエンドテスト方針も踏襲して、仕様書(Spec)に準拠した、ページ単位のインテグレーションテストを実装しました。この過程で以下の2点に関して、改良に取り組みました。
- 冗長なコードの回避
- バリデーションテストの最適化
冗長なコードの回避
Specに従ってテストを記述することで、チーム内でのテスト粒度や方針の一貫性を保てます。しかし、Specに厳密に従いすぎると、異なる画面で同じフォームコンポーネントを使う場合などに 検証内容が重複してしまい、コードが冗長になりがちです。
この問題に対し、以下の3つのアプローチを検討しました。
- 共通コンポーネントにテストを記述
メリット:テストコードの重複を解消できる。1つの共通コンポーネントに対するテストを集約できるため、同じ検証ロジックを何度も書く必要がなくなる。
デメリット:インテグレーションテストとしては「実際のアプリケーションに近い形でテストしたい」という方針とやや乖離する。複雑な部分をコンポーネント化してしまうと「人によってテストの書き方が異なってしまう」という懸念もある。 - 全ての画面でテストを記述:
メリット:上記二つの中間的アプローチで、各ページでの実際のユーザー操作を想定したSpecに基づいたテストを忠実に書くことになるため、微妙に異なるユースケースやバグを見落としにくい。
デメリット:同様のテストロジックを大量に書くことになり、変更があった際の修正も多岐にわたってしまいメンテナンスが大変。 - 代表的な1画面でのみ共通コンポーネントのテストを記述:
メリット: 上記2つの中間的アプローチで、テストの冗長性をある程度抑えつつ、基本機能の担保が可能。
デメリット: 完全な網羅性はないものの、必要に応じて各ページごとに追加テストを柔軟に書くことで補える。
最終的には、「代表的な1画面でのみ共通コンポーネントのテストを書く」方式をベースに、ページ固有のロジックがある場合だけテストを追記するという運用に落ち着きました。現状のチームリソースや開発速度を考慮すると、これが最も現実的かつ柔軟なアプローチだと判断しました。
バリデーションテストの最適化
フォームライブラリ(react-hook-form)の標準的なバリデーションはユニットテストでカバーし、インテグレーションテストでは、ユニットテストでは検証しにくいバリデーションに集中しました。
たとえば、以下のようにsubmit時にエラーがあった場合にモーダルを表示するロジックは、react-hook-formのschemaテストだけではカバーしにくいケースです。
const onSubmit = (value) => {
// 入力項目に誤りがある場合
if (value.name !== 'hoge') {
setShowModal(true)
}
// データの送信など
}
ここのような部分をPlaywrightを使ったインテグレーションテストで検証します。
// Playwright を使用したインテグレーションテストの例
test('入力項目に誤りがある場合にモーダルを表示する', async({page}) => {
// 省略
// ...
await page.getByLabel('名前').fill('foo');
await page.getByRole('button', {name: '送信'}).click();
await expect(
page.getByRole('dialog', { name: '名前にはキーワードを含めてください' }).toBeVisible();
});
テストを書くコストとリターンのバランスを意識しつつ、後々の技術負債にならないよう、意味のあるテストコードを心がけました。
また、開発プロセスの透明性と効率性を高めるため、インテグレーションテスト用のSlackチャンネルを作成しました。背景としては、フロントエンド領域で気軽に技術的な相談ができる場が十分に整備されていなかったことや、別チームのエンジニアとコミュニケーションを取る機会が少なかったことが挙げられます。そこで、実装中に直面した課題や疑問点を具体的なケースとともに共有できるようにしたことで、チーム全体で問題意識を共有することができ、より良いソリューションを見出すことができました。
その他の活動と経験
インターン期間中に、生成AIを活用した業務効率化アイデアソンにも参加しました。
90分という限られた時間内で、チームビルディングからアイデア出し、プロトタイプ作成まで行うというかなり濃密なスケジュールでしたが、とても刺激的で楽しかったです。
アイデア選定時は「共感が得られるか」と「短時間で成果を出せるか」を重視しました。最終的には、「カレンダーたのんだ〜」という、参加者の空き状況や入れたい予定の性質をもとにGoogleカレンダーの予定調整を効率化するアイデアに取り組むことにしました。
チームの方々が優秀すぎて、自分の役割を見出すのに最初は戸惑いましたが、自分にできる貢献を考え、ワークフローの設計および実装を担当しました。Zapierを使用してカレンダー情報を取得する部分も実装したかったのですが、時間の制約で叶いませんでした。
そして結果はなんと、優勝することができました🎉
(チームメンバーのみなさんに感謝です🙇♂️)
英語でのコミュニケーションでの苦労
インターン選考時の面接では、所属予定のチームは英語の使用頻度が高くないので、英語が苦手でも大丈夫というお話を伺っていました。しかしチームの状況が変わり、私が参加した初週から、週に1回フロントエンドのMTGは英語になりました。英語でのコミュニケーションには正直不安があり、特に司会役を担当しながら英語で進行しなければいけない回は、大変苦労したことを覚えています。事前にチートシートを用意するなどして、なんとか乗り切りました…。
さらに、オフィスには多くの外国籍社員が在籍しており、社内イベントへ参加することで自然と英語を使う機会が増えました。また、Pull Requestのレビューも英語でやりとりするため、日常的に英語に触れられる環境だと実感しました。
予想以上に英語を使うシーンが多かったことで最初は戸惑いましたが、そのおかげで英語学習のモチベーションが格段に上がりました。技術的スキルだけでなく、グローバルなコミュニケーション能力も磨ける環境は、エンジニアとしての成長に大きな価値があると感じています。
さいごに
今回、メルカリ ハロのインターンを通して、大規模サービス開発の現場で多くの貴重な経験を積むことができました。特に、インテグレーションテストの実装を通じて、効率的で品質の高いテストコードを書く考え方や、チームコミュニケーションの大切さを深く学んだと感じています。
この2ヶ月間で得た知識や経験を、今後の学びやキャリアに活かしていきたいと思います。最後になりましたが、メンターのd–chanさんをはじめ、あたたかく迎えてくださった皆さまに心より感謝申し上げます。