Testing Library の find*By系クエリは最初の1件でresolveするため、段階的に増える要素数の最終件数アサートには使えない
React Testing Library(および同系のqueries API)の findByText / findAllByText は「条件に一致する要素が現れた最初の瞬間」に resolve する非同期ヘルパーであり、「その後も要素が増減し続ける」ケースでは最終的な件数を保証しない。
典型的な事故: 通知(トースト等)を同一idで複数回発火し「重複が1件に纏まる」ことを検証するテストで、発火直後に await findAllByText(message) を呼び、その戻り値の .length をそのままアサートする形。DOMへの反映が1件ずつ段階的に進む実装(ライブラリが内部でsetStateを複数回叩く等)の場合、1件目が挿入された瞬間に findAllByText がresolveしてしまい、その時点のスナップショットでは「1件」に見える。これは「纏まったから1件」なのか「まだ挿入の途中だから1件」なのかを区別できない偽green(false positive)になる。実際、idによる重複排除を外した変異体(mutant)を投入しても、このアサーションは検知に失敗した。
判断基準: find*系は「存在の確認(出現を待つ)」にのみ使い、「個数の確定」には使わない。個数を確定させたいときは、(1) waitFor(() => expect(query*By...(...).length).toBeGreaterThan(0)) のように出現だけを非同期に待ち、(2) その直後に同期の queryAllBy...(Promiseを返さない)で件数をアサートする、の2段階に分ける。同期クエリはその時点のDOMスナップショットを返すため、非同期待機とは別に「今何件あるか」を確定的に問える。
適用条件: 対象の変更が複数回・段階的にDOMへ反映される可能性がある場合(同一状態への複数回のトリガー、リスト要素の逐次追加、ストリーミング更新など)に該当する。単発でしか要素が現れない操作ならfind*をそのまま使ってよい。
検証方法: 期待する重複排除・件数制御ロジックを意図的に壊した変異体を用意し、そのテストが壊れた実装で確実に落ちることを確認する(実装を壊しても通ってしまうなら、そのアサーションは空通りしている)。