レガシーシステムの画面リプレースで「移行したつもり」を防ぐ方法 — UIテストと機能パリティを仕組みで担保する

レガシーシステムの画面を、ReactやNext.jsなどのモダンな技術へリプレースする。

こうした案件で難しいのは、画面を作ることそのものではありません。

本当に難しいのは、

「移行後の画面が、移行前と同じように見え、同じように動くことを、どう確認し続けるか」

です。

画面が数枚なら、人が並べて確認する方法でも何とかできます。

しかし、対象が数十画面になると話が変わります。

しかも厄介なのは、画面全体が派手に壊れるわけではありません。

実際には、

  • 一覧表の中にあるボタン1つだけリンク切れしている
  • ボタンは表示されているが、押しても何も起こらない
  • エラー表示だけ旧画面とレイアウトが違う
  • 普段は問題ないが、特定の画面幅だけ表示が崩れる

といった、「1画面の中の1箇所だけが静かに壊れる」ケースが多くあります。

さらに怖いのが、

テストは成功、CIも成功、見た目の差分もなし

という状態でも、こうした問題が残ることです。

これは担当者が確認を怠ったからではありません。

単純に、

「テストで確認していた範囲の外だった」

というだけです。

そこで私たちは、画面リプレースにおける確認作業そのものを、できるだけ仕組みに変えることにしました。

この記事では、その中で特に重要だった考え方を紹介します。


「見た目が同じ」を人の感覚に任せない

画面移行で最初に問題になるのが、見た目の確認です。

一般的には、旧画面と新画面を横に並べ、

「だいたい同じ」

「少しズレている」

と人が判断します。

しかし、この方法には限界があります。

例えば、

  • ボタンの高さ
  • 余白
  • 入力欄の幅
  • テーブルの行高
  • フォントサイズ
  • 表示位置

などは、数ピクセル程度の違いなら簡単に見落とします。

さらに、CSS-in-JSなどを利用しているシステムでは、ソースコードを読んでも最終的なCSS値が分からない場合があります。

テーマや画面状態によって、ブラウザ上で初めてスタイルが確定するからです。

そこで、ソースコードを比較するのではなく、

実際にブラウザが計算したスタイル値を取得して比較する

方式にしました。


画面ごとに「何を確認するか」を先に決める

自動比較で重要なのは、ツールそのものよりも、

どの要素を見るのかを事前に決めること

です。

例えば一覧画面なら、

  • 件数表示
  • 検索条件
  • ページャー
  • テーブル
  • 更新ボタン
  • 削除ボタン

といった確認対象を定義します。

旧画面と新画面で、同じ役割を持つ要素を測定し、

  • サイズ
  • 余白
  • フォント
  • 表示位置
  • 枠線

などを比較します。

こうすることで、

「なんとなく似ている」

という判断を、

「どの項目が、どれだけ違うか」

という差分データに変えられます。

人が全画面を見比べるのではなく、

人が見るのは差分が出た場所だけ

という状態を作れます。


「測っていないのに成功」を防ぐ

ここで、自動テストを作ると必ず別の問題が発生します。

それが、

「実は測れていないのに成功になる」

という問題です。

例えば、比較対象を取得するための設定を間違え、

旧画面でも新画面でも対象要素が取得できなかったとします。

すると単純な比較では、

旧画面:データなし
新画面:データなし

なので、

差分なし

と判定される可能性があります。

しかし、本当は一致しているのではなく、

何も測れていないだけ

です。

そこで、

「取得できなかった要素が1つでもあれば失敗」

とします。

この考え方は非常に重要です。

fail-closeで考える

システムの確認では、

分からない場合は成功にしない

という設計が有効です。

例えば、

  • 測定データがない
  • 設定が古い
  • 片側しか測定されていない
  • 必要な状態が測定されていない

といった場合は、すべて失敗扱いにします。

「問題が見つからなかった」

「問題がないことを確認した」

は違います。

この2つを混同しないことが重要です。


通常状態だけでは不十分

もう1つ見落としやすいのが、

画面には複数の状態がある

という点です。

例えば、

  • 通常表示
  • エラー表示
  • モーダル表示
  • 保存完了メッセージ
  • データなし
  • 入力エラー

などです。

通常画面だけを比較しても、

エラー時の画面が完全に違っている可能性があります。

そこで、画面単位ではなく、

「画面 × 状態」

で確認します。

重要なのは、

「あとで確認しておいてください」

という運用にしないことです。

画面が持つ状態をあらかじめ宣言し、

必要な状態の測定結果が全部揃わなければ完了にできない

という仕組みにします。


数ピクセルの差をどう扱うか

ブラウザの画面比較をしていると、もう1つ難しい問題にぶつかります。

それが、

サブピクセルの丸め誤差

です。

同じ文字列でも、DOM構造やテキストノードの分かれ方によって、ブラウザが計算する幅にわずかな違いが出ることがあります。

さらに、devicePixelRatioなどの表示条件によって結果が変わるケースもあります。

ここで単純に完全一致だけを求めると、

本質的ではない1px未満の違いで大量にテストが失敗します。

一方で、何でも許容すると、今度は本当のレイアウト崩れを見逃します。

そこで、

サイズについては一定の許容誤差を設定しつつ、吸収した差分も記録する

ようにします。

例えば、

「差分なし」

ではなく、

「許容範囲内の差が8件ありました」

と表示します。

つまり、

テストは成功しているが、完全一致ではない

ということが分かります。

これは運用上かなり重要です。

許容誤差を入れる場合、

隠すのではなく、見える状態にする

ことがポイントです。


見た目と「動くかどうか」は別問題

UIリプレースでは、

見た目が同じでも機能が壊れている

ということがあります。

例えば、

  • ボタンを押しても反応しない
  • リンク先が404
  • フォームが送信されない
  • ページングだけ動かない

といった問題です。

そのため、

Visual Parity(見た目の一致)

Functional Parity(動作の一致)

は分けて確認します。


操作を「台帳」にする

機能確認では、画面ごとに操作一覧を作ります。

例えば一覧画面なら、

  • 検索
  • 検索条件リセット
  • 表示件数変更
  • ページ移動
  • 並び替え
  • 更新
  • 削除

などです。

それぞれにIDを付け、

旧画面と新画面で同じ操作を実行し、

結果を記録します。

ここで重要なのは、

新画面だけを確認しない

ということです。

例えば、旧画面に存在していたボタンが新画面では完全に消えていた場合、

新画面だけをテストしていると、

そもそもテスト対象として発見できない可能性があります。

そこで、

旧画面と新画面を両方開き、同じ操作を両側で実行する

方式にします。

これによって、

「旧画面にはあるが新画面にはない」

という問題も見つけやすくなります。


「押しても何も起きない」は静的チェックへ

すべてをブラウザテストで確認する必要はありません。

例えば、

「ボタンなのにイベント処理が設定されていない」

という問題は、コードを解析すれば分かります。

そこで、

  • ボタンにイベントハンドラがあるか
  • リンク先が解決可能か
  • form actionが存在するか

などは、静的テストへ移します。

この分け方はかなり重要です。

ブラウザテストは強力ですが、実行コストが高くなります。

逆に、コードだけで判断できるものまでブラウザテストにすると、

CI時間がどんどん増えていきます。

そのため、

静的に見られるものは静的に、実ブラウザでしか見られないものだけブラウザで見る

という役割分担にします。


変更した画面だけ再確認する

画面数が多いシステムでは、

変更のたびに全画面を確認する方式も現実的ではありません。

例えば57画面あったとして、

1画面の修正で57画面全部を再測定していたら、

確認コストの方が開発コストを上回ってしまいます。

そこで、

  • どの画面が変更されたか
  • 関連するCSSやJavaScriptが変わったか
  • 受入条件が変更されたか

を判定し、

影響する画面だけ再確認する

仕組みにします。

これにより、

品質を維持しながら、

確認作業を現実的な時間に抑えられます。


「何を確認していないか」を書く

この仕組みで最も重要だと感じているのが、

確認できていないものを明示すること

です。

テストの一覧表には、

「確認しているもの」

だけでなく、

「確認していないもの」

も書きます。

例えば、

  • hover時の見た目
  • focus時の表示
  • 受入シナリオ自体が十分か
  • 共通処理を経由した影響
  • 測定した環境が本当に正しいか

などです。

テストというのは、数が増えるほど安心感が出ます。

しかし、

テストが多いことと、すべてを見ていることは同じではありません。

むしろ危険なのは、

「CIが全部緑だから大丈夫」

とチーム全員が思い始めることです。

そこで、

緑が何を保証しているのか

を明文化します。


「見ていないものを減らす」と「テストを増やす」は違う

これも重要なポイントです。

見ていないものを発見すると、

すぐに新しいテストを追加したくなります。

しかし、これを続けると、

テストを維持するためのテスト

が増えていきます。

結果として、

開発よりもテスト基盤の保守に時間がかかる状態になります。

そのため、

新しいゲートを追加する条件を決めています。

基本的には、

「現在の成功判定が、誤った安心を生んでいるか」

で判断します。

成功判定そのものが嘘になる場合は直す。

一方、

「単に自動化できていないだけ」

なら、人の確認として残す場合もあります。

すべてを自動化することが目的ではありません。

何を機械に任せ、何を人が確認するかを明確にすること

が目的です。


証跡も「あとから書き換えられない」ようにする

画面移行の最終確認では、

結果だけでなく、

いつ、どの条件で、誰が確認したか

も重要です。

例えば、

  1. 実測
  2. レビュー
  3. 受入

という順序を記録します。

さらに、レビュー時点の測定結果をハッシュ化して保存しておけば、

レビュー後に測定結果が変更された場合、

過去のレビューをそのまま流用できない

ようにできます。

つまり、

「以前レビュー済みだから大丈夫」

という状態を防げます。

受入可否を、

人の記憶ではなく、

現在のデータ状態から判断できる

ようにするわけです。


実際にどこまで仕組み化したか

現在の運用では、数十画面規模の移行に対して、

  • 見た目の比較
  • 状態別の画面確認
  • 操作単位の動作確認
  • リンクやボタンの静的チェック
  • 変更画面だけの再検証
  • 独立レビュー
  • 受入証跡の整合性確認

までを組み合わせています。

特徴的なのは、

実装コードだけでなく、確認のためのコードもかなり大きくなる

という点です。

一見すると、

「そこまで作る必要があるのか」

と思うかもしれません。

ただし、レガシーシステムのリプレースでは、

新機能を作ることよりも、

既存動作を壊していないことを証明する方が難しい

ケースがあります。

特に業務システムの場合、

ボタン1つの挙動違いが、

業務停止や問い合わせ増加につながることもあります。

そのため、

確認の仕組みそのものを、

移行作業の一部として扱う必要があります。


まとめ:UIテストで重要なのは「緑の意味」を定義すること

画面リプレースで重要なのは、

テストを大量に書くことではありません。

私たちが特に重要だと考えているのは、次の5つです。

1. 「同じに見える」を差分にする

人の目で総当たりするのではなく、

ブラウザの実測値を使って比較します。

2. 測っていないものを成功にしない

未測定や設定漏れを、

「差分なし」と扱わないことです。

3. 許容誤差を隠さない

許容した差分も記録し、

完全一致と区別します。

4. 測定条件を証跡に残す

画面幅、表示条件、実行日時など、

「どの条件で成功したのか」を残します。

5. 人が確認する場所を明確にする

すべてを自動化するのではなく、

自動化できない部分を明示的に人へ割り当てます。

レガシーシステムの移行では、

「作った」ことと「移行できた」ことは同じではありません。

そして、

「テストが緑」だからといって、「旧システムと同じ」とも限りません。

大切なのは、

その緑が、

何を確認した結果なのかを説明できること。

画面数が増えれば増えるほど、

最後に品質を支えるのは、人の注意力ではなく、

こうした確認の仕組みそのものになっていきます。

あわせて読みたい

この記事に関連するサービス

記事の内容を実際の業務に落とし込むところから、ご一緒できます。

ここまで読んで、次にどうするか。2分で見当をつけるか、直接お話しするか、どちらからでも大丈夫です。

8問・約2分

まず自分で確かめる

いまいちばん手間な業務、減らせるかどうかが分かります

Excelの共有、紙の帳票、FAXの打ち直し、毎月の集計。いま手間な業務を1つ選んで8問に答えるだけです。

業務改善セルフチェックをはじめる

その場で結果が出ます。

オンライン30分

話しながら整理する

お気軽にご相談ください

ご質問やご相談がございましたら、お気軽にお問い合わせください。

無料相談はこちら

※ご連絡は原則メールでお送りします