「リプレイス」はリファクタリング
まずは、「リプレイスは実質リファクタリングである」という観点から始めます。そして、「リファクタリングにはリグレッションテストが必要だ」という話に進みたいと思います。
この過程で用語を統一していく必要があると考えています。しかし、「リグレッションテスト」という用語は目的を示す名称がついてはいますが、どのようなテストがリグレッションテストに該当するのか具体的に理解していない方も多いかもしれません。
そこで、リグレッションテストについても確認をしていきたいと思います。また、リプレイスを行う際にどのようなテストがあると便利で、どのようなテストがあると嬉しいのかについても確認し、リスナーの皆様にもご理解いただければと思います。
早速ですが、「結局すべてはリファクタリングに通じる」という話から始めましょう。
まず、『レガシーソフトウェア改善ガイド』という本があります。皆さんがそれをご覧になったかどうかはわかりませんが、レガシーなソフトウェアを改善するアプローチには様々な分類が存在します。その中で、リファクタリングとは具体的にはメソッドやクラスのレベルでコードの構造を変えることを指す、とこの本には記述されています。
本書ではモジュールやコンポーネントレベルでのリファクタリングを「リアーキテクティング」と呼んでいます。また、可能な限り高いレベルで行うリファクタリングのことを「リライト」という呼び方をしています。
リファクタリングという基本的な概念は一旦脇に置き、リライトのアプローチには様々なものがあることが述べられています。全体を一度に書き換える「ビッグ・リライト」と、段階的に実施する「インクリメンタルなリライト」がその例です。
話はまたコンポーネントレベルの導入や、それをどのように展開していくかに移りますが、結局これらのアプローチもリファクタリングの一環とみなされます。
具体的な実施方法として、ルーティングや画面単位で分割し、段階的に進める手法が挙げられています。
これは「インクリメンタルなリライト」という漸進的な方法ですが、これもリファクタリングの範疇に入るとされています。すなわち、これらの手法は根底にある目的やプロセスが同一であることが指摘されています。
リプレイスに関しては、これが実質的にリファクタリングの一部だということが明らかになったと思います。つまり、レガシーソフトウェアの改善において使われる"リプレイス"という用語は、リファクタリングの一環と考えていいでしょう。そして、リファクタリングにおける一般的な方法論がリプレイスにも適用可能だという観点から、この点を確認し、話を進めたいと思います。
リファクタリングにはリグレッションテストが必要
リファクタリングという用語については、また別の本によると「外部から見た時の振舞いを保ちつつ、理解や修正が簡単になるようにソフトウェアの内部構造を変化させることが、リファクタリングだ」と書かれています。
リファクタリングを始める際の最初のステップは、対象となるコードに対して充実したテスト群を構築することです。このテスト群はリファクタリングにおいて不可欠で、外部から見た振る舞いの一貫性を保証するために重要だと強調されています。
考えるべきは、どのようなテストが適切かということです。まず、書籍にあった定義を再度引用します。「外部から見た時の振舞いを保ちつつ、理解や修正が簡単になるようにソフトウェアの内部構造を変化させること」。
この理想を実現するためには、外部から見た振る舞いが維持されていることを確認する必要があります。機能的な面も非機能的な面も含め、期待される振る舞いが保たれているかどうかが重要です。
次に、意図しない問題が発生していないかを検証することです。たとえば、単にライブラリを更新しただけで、予期せぬ見た目の崩れが生じる場合があります。これを検証するテストは、リグレッションテストに相当します。
しかし、「リグレッションテスト」というのは非常に一般的な用語です。そこで、どのような特性を持ち、どのような単位で行うテストが有効かを検討したいと思います。
UI向けのライブラリを置き換える時、フレームワークを置き換えて、このような概念図になると思っていております。
このドーナッツの青い部分が、UIのライブラリというイメージです。
4つのテストのバリエーションを出していきます。
まずは一つ目です。
テストには、置き換え対象よりも外側の結合を確認するものがあります。ここで「外側」とは何かというと、現時点では置き換えが行われていない、まだ既存の状態のコンポーネントを指します。また、ルーティングの部分はまだ古い状態で手がつけられていないかもしれません。
さらに外側に目を向けると、ユーザー受け入れテストのシナリオが最も外側の層に位置していると考えても良いでしょう。
このような異なるレイヤーを持つテストが、最も重要なテストの一つです。
2番目のテストはコンポーネントの単位を置き換えようとしています。コンポーネントやUIのコンポーネントの実装に対するテストです。
3番目は、そのコンポーネントはライブの核となるロジックを呼び出すことがあります。また、ユーティリティ関数が含まれていることもよくあります。そのため、これらに関するテストも存在することでしょう。
第4のテストはイメージしにくいかもしれませんが、置き換えるべきコンポーネントの単位に対して行うホワイトボックステストです。
一昔前のReactであれば、エンザイムを用いたテストが一般的でした。これは、コンポーネントのライフサイクルに対して具体的なチェックを行うもので、内部の状態やステートを検証するものです。
リファクタリングを行う際に有用なテストについての質問に対して、前もって答えを出してしまいますが、これはあくまで個人の見解です。
リファクタリングに役立つのはどんなテスト?
リファクタリングが役立つと考えられるのは主に2つのパターンです。外部からの観察により振る舞いを検証できる点が挙げられます。
3つ目のパターンも、それほど悪くはありません。例えば、UIのライブラリを変更した際に呼び出し方が変わると、内部的な構造を調整するために実装を変更する必要がしばしばあります。そのような時に、テストが存在すると、うっかり起こりがちな問題から命を救われることもあります。
ですので、テストがあるというのは歓迎すべきことです。新たに書かなくても、既存のテストが役に立つ可能性は十分にあるわけです。
4番目は、ライフサイクルやそのステートに深く関わる部分をテストするような状況ですが、これは相当な労力を要します。
可能であれば、第2番や第3番のようなアプローチにシフトしたいところです。しかし、必要であれば気合いを入れて取り組む必要があるでしょう。
役立つ度合いで言えば、第1番と第2番のアプローチが最も望ましいと考えています。これを踏まえると、結論としては、テスト対象よりも上位の単位でブラックボックステストを行うことが一般に望ましいと言えます。
これまでの話をまとめると、「リプレイス」とは「リファクタリング」のことであり、リファクタリングにはリグレッションテストが必須です。リファクタリングにおいて役立つのは、置き換え対象より上位のブラックボックステストがあると、より良いというわけです。
リプレイスする時に整っていると嬉しいテスト
では、リファクタリングを行う際に整っていてほしいテストとは何かというと、開発者が変更により全体の動作を確信し、製品リリース時にユーザーに予期せぬ不具合を引き起こすリスクを最小限に抑えることができるような、自信と安心を提供するテストです。この二つを確保することが、何よりも重要です。
この目的を達成するためにどんな努力も惜しまないというのが、最後のピースかなと思います。
詳細は省きますが、ある大枠の構成があると、リプレイス(置き換え)について自然に対応できるようになると考えています。
ドワンゴの教育事業フロントでリプレイスを行った際も、完全ではないものの、上記の要素を満たしていたので、最も手軽にリリースできたのではないかと思います。
リプレイスのためには、開発者が自信を持って開発を進められるよう、リグレッションテストが非常に重要です。
このテストは高速でフィードバックを提供するために、自動化されている必要があります。また、品質保証チームと協力し、実質的なリグレッションテストを利用して、ユーザーへの影響を確認しながら進めることが大切です。
このようにして私たちは自信と安心を確保しています。皆さんの環境やサービスの特性に応じて、この自信と安心を構築する方法は大きく異なるでしょう。
実際には、省ける部分もあるかもしれません。ですので、ご自身のサービスや管理しているコードベースにリプレイスを適用する際には、この2つのポイントを念頭に置いて考えることで、リプレイスの進行やリリースがよりスムーズになると思います。
皆様の成功を祈っています。