追記(2019/11/2)
今回の記事で深く扱えなかったリファクタリングに関する記事を書きました。
「汚いコード、綺麗なコードって何?」リファクタリングを考えてみる
テスト駆動開発とは
テスト駆動開発とは、**「テスト => 実装 => リファクタリング」**という流れを何回も何回も繰り返してプロダクトを成長させていく開発手法です。
もう少し詳しく説明します。次の3段階を繰り返します。
1. テストケースを考えるフェーズ
これは設計を考えることに等しいフェーズです。このオブジェクトがどんな機能を持っていて欲しいか、どういう風に使われるか、そしてその時にどんな結果を返して欲しいか、という思いをテストケースに込めます。
重要なのはこの時に実装のことは一切考えず、どう使われるかを考えることです。
2. 実装するフェーズ
テストをパスするためだけの最低限度の実装をします。この時も抽象化やキレイなコードなどを考えてはいけないのがルールで、とにかく1で書いたテストを通すことだけを考えて実装します。
極端にいうと sum(1,1) => 2
というテストを通したい時にはsumの実装はただ2を返すだけのメソッドにしてもいいわけです。
3. リファクタリングするフェーズ
2のフェーズで犯した罪を償うフェーズです。抽象化できるところを抽象化したり、酷い変数名を直したり、コピペしたところをメソッド化したりします。
この時のポイントは1箇所直したらすぐにテストを実行して、テストがパスすることを確かめながらリファクタリングをすることです。これによって振る舞いが変わっていないことを確かめながらコードをリファクタリングすることができます。
なぜテスト駆動開発をするのか?
かなり異質な開発手法なので、初見の人にとってはかなりとっつきにくいというか、なんでそんなことをするのだろう?という風に思われると思います。
しかしテスト駆動開発はアジャイル開発の基本的スタイルであり、一度やり方に慣れてしまえばむしろ「なぜテスト駆動でやらないのか?」という風に思うようになります。
今回の記事では、なぜテスト駆動開発をやるのかという理由を言語化してみて、その中でも自分が重要だと思った4つを紹介したいと思います。
1、高い品質でプロダクトを作ることができる
一番わかりやすいのはこれだと思います。
テストを書いてから実装をするので、一般的にテスト駆動開発のテストカバレッジは100%に近い値になります(リファクタリング時にメソッド切り出しなどをするとそこがテストされないことがあり得ます)。
それだけでなく、テストを書くフェーズでバグのことを考えることができるのも品質を向上させることに一助しています。テストを書くというのは利用時のことを考えることになるので、「もしここの値がnullだったらエラーを返して欲しいな」など利用時に起こりうるエッジケースの発想が湧きやすくなります。結果的に早い段階でエラーケースをカバーできることがあります。
2、必要なものを必要なだけ実装できる
実装フェーズでは、現在パスしていないテストをパスすることだけを考えるので、テストケースに表現されていない仕様は実装しません。
テストを書かずに実装から始めると、よくあるのは過度な抽象化をしてしまうことがあります。もしかしたら今後使うかもしれない、と思って余計な機能を足してしまい、結果使われなかったということがよくあります。
余計な機能を書いてしまうというのはコードの可読性を下げるだけでなく、バグを生み出しやすい原因になります。
実装しすぎも悪いですが、当然実装しなさすぎ(仕様漏れ)もよくありません。テストケースでしっかりと仕様が表現できていれば、実装側はテストとシンクロしているので仕様を全て満たしていることが確認できます。理想は仕様書のドキュメントを書かず、テストを仕様書にすることです。仕様書のドキュメントに書かれているけど実装されていない、のようなギャップは、テストそのものを仕様書だとした時には絶対に起こりえません。
3、リファクタリングを安心して行える
リファクタリングは全てのテストがパスしている段階で行っていきます。
そして1箇所変更したらすぐにテストを回して、全てのテストがパスしたままかどうかを確認します。
これによって**「自分の行った変更が他の箇所に影響を与えていないか」というリグレッションテストができている**ので、安心して変更をすることができるようになります。
その自信が大胆なリファクタリングを後押ししてくれます。
4、安全に確実に短いステップで開発できる
テスト => 実装 => リファクタリングを繰り返すことでゆっくりとインクリメンタルに開発を進めていくことができます。
途中で大きく詰まってしまったり、他のテストを壊してしまっても、全てのテストをパスするところまでundoすればやり直せます。
つまりテストがセーブポイントになるわけです。
短いステップで開発をすることは他にもメリットがあります。人間は大きく物事を考えることが苦手です。「〇〇を作ってくれ!」と突然言われると、人は白いエディタの上にいきなり放り出され、どこから何を始めればいいのか全く分からず、最初の一歩さえ踏み出せなくなります。しかし「〇〇ということはまずこういう仕様があるよな」という風に考えてテストケースをかき、それだけを実装することに専念をすれば一歩を踏み出すことができます。人は問題を小さくして、それにだけ集中すればできる生き物です。短いステップとは、そういうスコープの切り出しの意味もあるわけです。
最後に
テスト駆動開発をペアプログラミングでやることは、プログラミング教育にも向いていると考えています。
熟達者がテストケースを書いてそれを学習者が実装し、リファクタリングの段階で熟達者が指導することで技術力が向上します。
テストケースの歩幅を熟達者が調整できるので、難易度を調整しながら進めることができます。
また従来のように結果しか見ないプログラミング課題を解かせるよりも、プロセスを見ながらフィードバックをすることも教育的効果が高いと考えています。プログラミングは結果よりもむしろそのプロセスの方が重要で、そこが改善することで生産性が高まると思っています。
それでは。