すでに生み出されて動いているレガシーコード(テストのないコード)との戦争。結局武器はテスト


スピード感重視なのでテストは書かない。テストはなぜ開発を遅くするか というという記事がバズって以降テストの記事ばかり投稿しています。この際だからテストに関するもやもやを吐き出しておきます。

上記記事にてToDoとして、
すでに生み出されて動いているレガシーコードにどう立ち向かうか
と書きました。

一回レガシーコードと戦って勝利一歩手前まで行った経験があるのでその経験をまとめます。

どんなコードだったか

  • 規模は小さめ
  • ユニットテストなし
  • 自動テストなし
  • テストはデプロイしないと不可
  • 前任者(作成者)いない
  • UIなし

やりたいこと

テストがあれば一時間で終わる程度の変更を加える

格言

レガシーコードの改変について有名な格言があり、

レガシーコードのジレンマ

コードを変更するためには、テストを整備する必要がある。多くの場合、テストを整備するためには、コードを変更する必要がある。

「レガシーコード改善ガイド」マイケル・C・フェザーズ

上記の理由で、基本的にレガシーコードは詰んでいることが多いです。このときは幸運に恵まれていたため、そのあたりを解説していきます。

幸運と方針

このとき幸運だったのは、UIがない、IOが(量は多いけど)シンプル、リポジトリ単位では独立している、という点でした。

方針は、コードを変更する必要がない最小の単位で自動テストを整備すること。
すなわち、リポジトリ全体の自動試験を整備すること、です。

やったこと

docker-composeを用いて関係するIOをすべて再現して、IOが一致することを確認するプログラムを書きました。
docker-composeなので、自動実行です。
テストにテストが必要なレベルですが、たぶん仕方ない。

この手のテストを書く際の注意点

  • 直感的におかしくても、レガシーコードの出力が正。
  • できる限りケースを網羅する。
  • エラーで落ちるパターンだけは何とかする。

上記が完成したら

テストコードが正しいことを常に確認しながらユニットテストを整備していきます。
できる限りこまめに上記テストを実行するのがポイントです。
簡単そうに書きましたが大変です。

ユニットテストの整備

基本的にこのようなコードはユニットの概念がないため、ものすごく苦労します。中身は密に結合しているため、一箇所に変更を加えた際に、全く意図しないところがエラーを吐く、というのがザラでした。
最初は、テストを定期的に実行しながら、読みやすくするために等価っぽい変更を繰り返します。見通しが良くなるまでは、我慢します。

次に、捨てブランチを切って思うがままに変更してテストします。このブランチはどんなにうまく行っても必ず捨てますが、代わりに何をしても構いません。たいがい適当に変更するとぶっ壊れます。

程々に挙動がわかってきたら本番の変更用ブランチを切り、微妙に疎になっている部分を見つけてちょっとずつ切り出し、ユニットテストを書きます。

最終的に

重要なところのユニットテストを書きおわったあたりで担当を離れることになったのでこのプロジェクトが最終的にどうなったかは知りません。

まとめ

結局テストを書かなければレガシーコードとは戦争できない。