紡Book の勉強ノートに戻る
技術ブログ2026.07.28

テストが3本とも落ちたのに、落ちた理由が違った — 赤を確認しただけで実装に入りかけた話

テストTDDReactNext.js紡Book

作成画面のプレビューに写真の言葉が1件しか出ない、という穴を塞いだときの記録です。手順としては教科書どおりに進めたつもりが、途中で足元が抜けていた。

写真1枚に言葉を何度も重ねて書けるようにしたので、まず1つのテンプレートを、その全部を出す作りに直したところだった。保存したページと共有リンクでは出る。でも作成画面のプレビューだけ1件しか出ない。

原因はすぐ見えた。プレビューはサーバの解決を通らず、その画面でローカルに組み立てています。だから言葉の束が届いていなかった。この穴に気づいた経緯と、同じ晩にもう1面数え漏らしていた話は「「描く面を全部数える」と書いた同じ晩に、自分が数え漏らした」に書いた。

先にテストを書いた

直す前に、画面まで通したテストを3本書いた。関数だけを取り出したテスト(画面を出さずに、入れた値と返る値だけを見るもの)だと、関数が正しくても画面へ繋ぐ配線が抜けたら気づけません。実際その穴を踏んだところなので、今回は画面を実際に描かせて測ることにした。何を確かめる3本にするかはこちらで決めて、テストのコード自体は AI に書いてもらっている。

tsx
it('2本目の綴りが出る', () => {
    gotoPreviewWithPillarMarks(); // テスト用のデータでプレビュー画面を開くところまで進める
    // 画面の中に「あとで重ねた一文」がちょうど1つ出ているか
    expect(screen.getAllByText('あとで重ねた一文')).toHaveLength(1);  // expect(値).マッチャー(期待)の形
});
 
it('2本目に「◯月◯日 に追記」が添う(1本目には付かない)', () => {
    gotoPreviewWithPillarMarks();  // 同上=プレビュー画面を開く
    // 追記の日付が画面に出ているか。無ければここで落ちる
    expect(screen.getByText('7月25日 に追記')).toBeInTheDocument();  // toBeInTheDocument=存在するか
});
 
// これは配線と関係ない。直す前でも緑のはず。
it('1本目は本文のまま出る(代表が消えない)', () => {
    gotoPreviewWithPillarMarks();  // 同上=プレビュー画面を開く
    expect(screen.getAllByText('最初に綴った一文')).toHaveLength(1);  // 1本目が1件のままか
});

走らせたら3本とも赤になった。「よし、赤を確認した」で実装に入ろうとした。

3本目が落ちるのはおかしい

手が止まったのは、3本目が落ちたからだった。

3本目は「1本目の本文が出る」だけを見ている。これは今回の修正とは無関係で、直す前でも通っていないといけません。それが落ちているということは、赤の理由が想定と違う。

プレビューの中身を出してみたら、答えはすぐだった。

html
<div class="_preview_849d30" />

空。何も描かれていない。

後から読み込まれる画面は、その場で確かめても間に合わない

テンプレートの実体は next/dynamic(Next.js の「後から読み込む」書き方)で遅延ロードしています。テンプレートごとに CSS を分けておくための決まりで、テンプレートを振り分ける側(プレビューも保存後のページも共有面もここを通る)に実体を普通の書き方で読み込む行が1本でも残ると、全テンプレートの CSS が1つのまとまりに束ねられてしまいます。

綴りの材料はテストのメモリ(モック)。プレビューの箱は jsdom。DB は通らない。保存しない。テストのメモリモックの2本の綴り最初に綴った一文あとで重ねた一文保存しない。DBではない組み立てjsdom のプレビュー空の箱押した直後の中身何も描かれていない保存しない未着テンプレ実体まだ未読込遅延ロードの先ファイルのチャンクまだファイルから届かない押した直後に見ると、空の箱を見る。実体は後から届く。遅延ロードは交差であり層ではない。

待つ形に直した。

tsx
// find* は要素が現れるまで待つ。get* はその瞬間を見るだけ。
expect(await screen.findAllByText('あとで重ねた一文')).toHaveLength(1);  // 描画を待ってから件数を確認

走らせ直すと、本題の2本だけが落ちて、3本目は緑になった。これが正しい赤だった。

画面の状態は jsdom のメモリ。綴りの材料はモック。DB は通らない。保存しない。想定(置き場所ではない)本題2本は赤。対照の1本目は直す前でも緑。初回の jsdom空のプレビューを見る2本目の綴り 赤追記の日付 赤1本目の本文 赤3本とも同じ理由=空待ち後の jsdom描画後のプレビューを見る2本目の綴り 赤(本題)追記の日付 赤(本題)1本目の本文 緑対照だけ緑=土台は生きている

赤になったこと自体は、何も証明していない

「まず赤を確認する」はテストの基本として何度も聞きます。でも赤には理由が要ります。想定どおりの理由で落ちているのか、まったく別のところで落ちているのかは、赤という結果だけでは分かりません。

もし3本目を書いていなかったら、赤2本を見て「よし配線が無いから落ちてる」と納得して、空の画面を相手に実装を進めていた。修正が効いているのか、待ち方の問題が解けただけなのか、最後まで切り分けられなかったと思う。

今回いちばん効いたのはこれ。

初回のランに「直す前でも緑のはずの1本」を混ぜておきます。 それが落ちたら、赤の理由が別のところにあります。

効くのは、その1本が本題と無関係で、かつ他と同じ手順で画面を立ち上げているとき。土台が崩れていれば道連れで落ちる位置にいるから、崩れが見えます。画面まで通すテストは、関数だけを取り出したテストと違って「まだ配線していない」以外の理由でも落ちます。その分だけ、対照の1本が要ります。

この手順は続けていて、8月には今度は赤にできない側で2回止まった(「「直す前に赤にする」を守ったら、同じ日に2回、赤にできなかった」)。

この記事は2026年7月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。