テストコードの変遷とテストを書く文化創りのためにやったこと

LiBzCAREER の立ち上げから約2年間のテストコード (RSpec) の変遷についてLT した。

なぜ、プログラミングはつらいのか?

  • コードを書いた分だけバグが出る
  • コードは触らないと腐っていく
  • 今は素晴らしいコードもいつか邪魔になる
  • 仕様変更は簡単だけど、同じ手軽さでコードは変わらない

テストコードがあればこれらの問題が解決するわけではない。テストはモニタリング手法であり、コードの状態を見える化するものです。コードの品質を良くするには、設計と実装を頑張るしかないのです。

あなたは、なぜテストを書かないのか?

テストを書かない理由はたくさんある。

  • テストを書く時間がない
  • 実装コードがダメだから、テストが書けない(書きづらい)
  • まだ仕様が曖昧だからテストが書けない
  • テストを書くより実装の方が楽しい
  • ちゃんと動いてるから良くね?

それでもテストが必要になる。コミットするときは少しでもリファクタリングをして、今よりも良いコードを書いていかないとサービスを継続的に提供し続けられなくなる。

いずれ苦痛を伴う選択を迫られるのだから、今のうちから辛くても良い結果を生む選択をすべきだと思います。コードを書いたときから終わらないリファクタリングが始まっています。

初期ローンチ

初期ローンチはサービスの企画から設計、実装、テスト、リリースまで約1.5ヶ月間で正直RSpec を書いている余裕がなかった。言い訳ですが想像以上に余裕がなかった :sweat_drops:

初期ローンチのテスト戦略

テストの目的として、

  • 自分の思考外の正しいデータでサービスが落ちないこと
  • 主要な機能がデグレしないこと

の2つを意識していました。

サービス全体を効率よくテストするために、 factory_girl を使ってテストデータを生成しました。テストシナリオなどもなかったので、自分の思考外のバグを見つけるために、正しいデータをランダムに生成するようにしました。

具体的なテストのやり方は、100万レコードくらいの大量データを生成して、画面を触りながら予期せぬところでサーバーエラーが出ないか動作確認してバグを潰しました。一部のバグは再発防止策としてテストコードを書き起こしました。考慮漏れのバグなので同じミスしそうだなーと思ったので。

ひとりで開発

サービスをローンチしてから約1年間は機能追加しつつ、ある程度テストコードを書きつつCircleCI でテストを回すようにしました。チームで開発する前に、CI がワークフローに組み込まれていてテストがグリーンである状態にしておきたかったのです。あとから導入するにはチーム全員のメンタルモデルが障害になるので。

チーム開発を見据えたテスト戦略

ひとりで開発していたものの、いづれチーム開発になるので、

  • ワークフローにテストが組み込まれていること(継続的インテグレーション)
  • リカバリーの効かない機能のデグレを防ぐ
  • 画面テストでは時間がかかる機能のテストを自動化する
  • 仕様理解の浅いメンバーが想定外の影響範囲でデグレさせないこと

を意識しつつテストコードを書くようにしました。

正しいテストのお作法でもないし、けして綺麗なテストとは言えないが、Controller を中心にテストを書くことでなるべく広範囲のコードが実行される様にしました。 render_views を指定すれば、View のレンダリングまで確認できます(デメリットとして実行時間が長くなります)。注意したいのは、広範囲のコードが実行されるだけで広範囲のコードがテストされているわけではないです。

また、メール機能は積極的にテストコードにしました。メール機能の改修は文言変更だけのことも多く、その度に画面で確認するには手間がかかります。そして、画面と違ってメールは一度送ると取り返しがつかないので、ミスしないように注意が必要です。

チームで開発

全体最適を考えたテスト戦略

チーム開発では、テストを書かない言い訳をさせないように注意が必要になります。

  • 人が見なくて良いものはコンピューターに任せる(静的解析ツール)
  • 実行時間の長いテストや高頻度で落ちるテストは優先的に改善する
  • 既存のダメなテストは捨てる

RuboCop でRuby のコーディングスタイルやBrakeman でセキュリティのチェックをCI に組み込んでいます。これらのチェックは人が毎回やるのは面倒なので機械的にチェックしてfail させるようにしています。静的解析ツールを使うことでコードレビューはもっと本質的な観点に絞ることが出来ると思います。初めは全部ignore 設定すれば導入しやすく、少なくとも今よりも悪化しなくて済みます。

あと、テストを書く文化ができてくるとテストを書くことが目的になってしまったりするので注意が必要です。過去のテストコードでダメなものがあれば、無理にメンテナンスせずに捨てて書き直した方が良いこともあります。テストコードを捨ててもサービスは動きます。

テストコード文化を創る

「みんな頑張って書こう」では進まないので、仕組みで解決して行く必要があります。人は易きに流れるので、CircleCI でもJenkins でもワークフローに組み込みテストが通らないと進まない状態を作るのが第一歩だと思います。最初はテストが空っぽでも良いと思います。