丁寧に学ぶフロントエンドアーキテクチャ：第5章「テスト設計」

# 目次

この記事は、丁寧に学ぶフロントエンドアーキテクチャの第5章です。
**いいね・ストックをよろしくお願いします！**

https://qiita.com/wolfmagnate/items/f2044201266e48cdd299

# はじめに
テスト設計は非常に重要です。
なぜなら、ソフトウェア開発中に作成するテストは、大抵最初に書いたものをコピペして使うことになるからです。また、最近では、AIもそれをマネして作るため、事前に設計し、適切なテストを書くことができる基盤があることが大切になってきたのです。

# BFFのテスト
## BFFのテスト設計
BFFのテスト設計をする際に重要な特性を説明します。

:::note
**BFFのテスト設計方針**
バックエンドと同じように、シンプルな単体テストが重要
:::

### 複雑
BFFはバックエンドの機能を論理画面に変換する処理を担います。したがって、データ構造の複雑な詰め替えや整形処理を多く含む可能性があります。バグを生みやすく、網羅的なテストを行うべきです。

### 軽量
BFFはブラウザを意識しない処理を行います。したがって、BFFの処理は軽量であり、品質の確認にIOを必要としません。単体テストを実現可能です。

### 責任範囲
BFFは機能提供側のバックエンドエンジニアと、利用者側のフロントエンドエンジニアのどちらも関与する領域です。したがって、そのテストはバックエンドエンジニアでも十分に理解可能である必要があります。フロントエンドエンジニアにしか分からない仕組みは極力減らし、シンプルな単体テストにするべきです。

## BFFの依存関係
第4章で解説したように、BFFは主にバックエンドとセッションストレージに依存しており、テスト時には適切に`vitest`の機能を利用してモックすることでDIを行います。

## 単体テスト例
テストの内容は、テスト対象コードの意味内容によって大きく異なるため、ここでは説明できません。ここでは、簡単なブログ管理APIを例として、モックを利用したBFFの単体テストの例を説明します。

```typescript
import { describe, test, expect } from "vitest";

describe("Blogposts API", () => {
  test("GET /blogposts/{post_id} で記事を取得する", async () => {
    // モックの戻り値を設定 (2XXレスポンスを想定)
    mockedClient.GET.mockResolvedValueOnce({
      data: { title: "Mocked Title", content: "Mocked Content" },
    });

    // テスト対象のコードを呼び出す処理
    // 実際はここでBFFを呼び出すため、戻り値の型は複雑であることが多い
    const response = await getBlogPostById("123")

    // モック結果とレスポンスをテスト
    expect(response.data?.title).toBe("Mocked Title");
    expect(response.data?.content).toBe("Mocked Content");
    expect(mockedClient.GET).toHaveBeenCalledWith("/blogposts/{post_id}", {
      params: { path: { post_id: "123" } },
    });
  });
});
```

# Viewのテスト
## Viewのテスト設計
BFFと違って、Viewはブラウザ・DOMを意識した処理です。したがって、どうしても重たい処理になることを避けられず、単体テストを行うことができません。そのため、**少ないテストケースで効率よく主要な部分をテストすること**が重要です。
さらに、Viewで重視される品質特性は論理的な正当性だけではありません。ユーザビリティや見た目の再現度など、デザイナーがかかわらないといけない品質特性のテストもあります。そのため、単に網羅的に正しいことを検証するのではなく、**目的がはっきりとしたテストにすること**が重要です。

:::note
**Viewのテスト設計方針**
ブラウザと目視が避けられない。単体テストが難しいことを前提に、効率と効果を追求する。
:::

テストの性質ごとの方針を簡単に示します。

![5-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/7f1b2869-12b1-4c70-ab6d-4c9a95ebe068.jpeg)


## 視覚的品質のテスト
デザインの再現度を確かめるテストです。具体的には、次のような項目を確認します。

- 色やタイポグラフィの再現ができているか？
- 表示崩れは無いか？
- レスポンシブデザインは適切に機能しているか？
- アクセシビリティは十分か？

これらの視覚的な特性は、ユーザビリティの一部を構成します。このテストは、フロントエンドエンジニアとデザイナーの共同の責任領域のテストであるため、どちらでも簡単に実施できる必要があります。エンジニア特有の知識が無くてもテストができる環境を整備するべきです。

- デザイナーは、意匠を担当しているため、現時点の再現度で満足できるかを確認したい
- エンジニアは、実装を担当しているため、正しく描画されているかを確認したい

### 手動テストと自動テスト
視覚的品質のテストは、定義からして、人間が目視で確認する必要があります。そのため、テストの工程に人間が必要な手動テストをすることになります。

ただし、一度人間が確認したものから意図せず変更されていないかは自動的に検証することができます。いわゆるビジュアルリグレッションテストと呼ばれるテストです。事前に正しいレンダリング結果を画像として保存しておいて、画像の差分を比較することで変更を検出します。

## 視覚的品質のテストの実装
この節から、具体的な実現方法について説明します。
### Story Book
視覚的品質のテストにStoryBookを使います。
Story Bookは、隔離された環境でUIコンポーネントを独立して表示し、テストするためのツールです。普通に開発サーバーとブラウザを組み合わせて表示するのに比べて、次のようなメリットがあります。

- アプリケーション全体を操作せずともUIを確認できる
- コンポーネント単位で視覚的品質を確認できる
- デザイナーが扱いやすい
- 一覧性が高い

これらの性質により、Viewのテストを効率よく支援することができます。

### 論理画面のモック
Viewは、論理画面を受け取って物理画面を構築します。論理画面を受け取る経路は、2種類存在するのでした。

- 親コンポーネントからの情報を受け取るためのProps
- 動的に情報を取得するためのAstro Actions

視覚的品質のテストでは、これらの論理画面情報を適切に与えることによって、Viewに物理画面を表示させます。PropsはStory Bookの機能で比較的簡単に情報を渡すことができますが、Astro Actionsは未だ標準的な仕組みが整備されていません。現時点ではHTTP通信をモックすることで情報を渡す仕組みが現実的です。
第4章で述べた通り、`Mock Service Worker`（MSW）との連携によって、Astro Actionsの動作をモックします。MSWは、Service Workerをブラウザの中に登録し、全てのネットワークリクエストを監視します。そして、事前に登録しておいた通信が要求されたら、実際にリクエストを発生させることなく、指定されたハンドラが値を返すことで、HTTP通信をモックします。

![5-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412657/34e3b8cb-a997-4aa5-8217-e35ffeb13201.jpeg)


技術的な詳細については説明を省きますが、`Controls`アドオンによりPropsを再現し、`MSW`アドオンによりAstro Actionsの通信結果を再現しています。

### 視覚的品質のテストの範囲
視覚的品質のテストは、どうしてもブラウザと目視が必要であるため、コストが高いです。そのため、利用はなるべく静的な見た目だけの検証に抑えるべきです。ユーザーインタラクションの動作検証などは、可能な限り次節で説明する機能テストで行うことで、視覚的品質のテストの範囲を最小化することができます。

ただし、次のような場合では視覚的品質のテストは有用です。

- 開発段階で見ながら調整する場合
- デザイナーとの共通言語として利用する場合

:::note
**視覚的品質のテストの範囲**
- なるべく1画面につき1テストにする
- 複雑な動作シナリオによる遷移など、画面同士の関係性は機能テストで検証する
:::

## 機能テスト
物理画面の特性のうち、見た目とは無関係な部分は、機能テストでテストします。例えば、次のような項目をテストします。

- ユーザーインタラクションにより適切に物理画面状態が遷移するか
- 物理画面のイベントが適切にServer Actionに変換されるか
- Server Actionから返された論理状態が適切に反映されるか

テスト項目に見た目が介在しないため、テストを自動化することができます。

### testing library
`testing library`はアプリケーションのコンポーネントをユーザー視点でテストするためのツールです。従来のテストライブラリと異なり、内部の実装詳細に依存せず、実際のユーザー操作に近い形でテストを行うことを重視しています。

https://testing-library.com/

次のような機能を備えており、`vitest`などの単体テストツールに比べるとブラウザを意識したツールであることが分かります。

- 内部的にレンダリングを実行する
- 表示されている要素に対してクリックなど操作を行う
- 操作結果をDOMレベルで取得、テスト

`testing library`でテストできる内容は、DOMの変更内容など、Viewに関連したものです。しかし、あくまでDOMの構造までしか検証できないので、機能テストに適しています。

## Astroコンポーネント
Viewのテストでは`Storybook`と`Testing Library`を紹介しました。しかし、Astroコンポーネントはこれらのツールとの相性が悪く、併用が難しいです。

Astroコンポーネントは、サーバーサイドで描画内容が決まる静的なコンポーネントであるため、`Testing Library`との相性の悪さは比較的問題になりづらいです。なぜなら、機能テストが必要になるほど複雑に操作するようなコンポーネントは、UIコンポーネントとしてReactで実装するべきだからです。

しかし、`Storybook`との相性の悪さは問題です。今のところ視覚的品質のテストをAstroコンポーネント単位で行うことができません。

`Astrobook`という簡易的な代替も用意されていますが、エコシステム（資料の数、アドオンなど）が足りておらず、本格的な利用はいまだ難しいです。将来的な機能追加を待ちたいところです。

https://github.com/ocavue/astrobook

# 統合テスト・E2Eテスト
フロントエンドが関連するテストとして、統合テストやE2Eテストがあります。
これらのテストの詳細についてはフロントエンド以上の内容を含むため、軽く説明するにとどめます。
基本的には、レイヤー分けを活用して個別にテストして、統合して初めてわかる内容以外はテストしないことが重要です。
重要な品質特性は次のようになります。

## パフォーマンス
パフォーマンスは、ユーザー目線の品質です。いくら個別のコンポーネントが高速でも、ユーザーから見たアプリケーションの動作が十分に高速でなければ品質が不足しています。逆に、内部的に低速な部分があっても、ユーザーの体感速度に影響しなければ問題ではありません。

## ユーザビリティ
モックの作成や机上での設計段階で十分に検討するべきです。
しかしながら、やはりどうしても動かしてみないと分からない手触りというものが存在します。

# おわりに
本章では、アーキテクチャのレイヤーごとにフロントエンドテストの手法を説明しました。レイヤー分けとDIによって、高速かつ自動的にテストが実施できるようになったことが感じられると思います。

https://qiita.com/wolfmagnate/items/0401a80cf7aab2ae460e