🐛

TDD:テストを起点にした安全なコード設計!

はじめに

テスト駆動開発(TDD)とは何か

皆さんは、実装を進める際、どのような手順で開発を進めていますか? いきなりソースコードを書いたり、まずは設計を行ったり、あるいはテストを書いたり、というように、開発の手順は様々です。
今回は、TDDという開発手法について紹介します。
東京ディズニーランドのことではありませんよ! 「テスト駆動開発(Test Driven Development:TDD)」のことです。
本記事は、TDDの基本的な考え方やメリットについて紹介します。 参考文献として以下の動画を参考にしています。

TDDの基本サイクル(Red-Green-Refactor)

TDDは、以下の3つのステップを繰り返すことで開発を進める手法です。 1. Red(赤):テストを書いて失敗させる 2. Green(緑):テストを通す実装を書く 3. Refactor(リファクタ):コードを整理する
開発現場では、red-green-refactorというキーワードで表現されることが多いです。ちょっと信号機みたいですね🚥
それぞれのステップについて詳しく見ていきましょう。
  1. Red(赤):テストを書いて失敗させる まずは、テストを書いて失敗させることから始めます。 このステップでは、まだ実装が存在しない状態で、テストが失敗するような状態を作ります。 このステップで、テストが失敗することで、次にどのような実装が必要かを明確にします。
  1. Green(緑):テストを通す実装を書く 次に、テストを通す実装を書きます。 このステップでは、テストが成功するような実装を書くことが目標です。 このステップで、テストが成功することで、実装が正しいことを確認します。 この時点では、RedからGreenに移行するだけで、実装の品質や効率は考慮しません。
  1. Refactor(リファクタリング):コードを整理する 最後に、コードを整理する作業を行います。 このステップでは、コードの重複を除去したり、可読性を向上させたり、保守性を改善するためのリファクタリングを行います。 このステップで、コードの品質や効率を向上させることが目標です。
    1. ここでは、リファクタするたびにテストを実行し、テストが成功することを確認しながら進めます。 整理する箇所は、コードの品質や効率を向上させるために必要な箇所に限定し、過剰なリファクタリングは避けます。

TDDのメリット

TDDのメリットは以下の3つがあります。それぞれコード駆動開発と比較しながら考えていきましょう。
  1. テストの自動化 📝
  1. リファクタリングの促進
  1. 設計の改善 🔨
 
  1. テストの自動化📝 TDDでは、テストを最初に書くため、テストが自動化されます。 これにより、テストを繰り返し実行することが容易になり、バグの早期発見やリグレッションテストの実施が容易になります。
    1. 一方、コード駆動開発では、テストを後から書くため、テストが手動で行われることが多く、テストの網羅性や再現性が低くなる可能性があります。また人が手動で画面を操作しながらテストすることになるため、テストの実行に時間がかかります
  1. リファクタリングの促進⚡ TDDでは、リファクタリングが開発サイクルの一部として組み込まれています。 これにより、コードの品質や保守性を向上させるためのリファクタリングが促進されます。
    1. 一方、コード駆動開発では、リファクタリングが後回しにされることが多く、コードの品質や保守性が低下する可能性があります。
  1. 設計の改善🔨 TDDでは、テストを最初に書くことで、設計を考えることが重要となります。一番はじめにどのようにテストを書くのか考えることになるため、設計が先行することになります。 これにより、設計の改善やコードの品質向上が促進されます。
    1. 一方、コード駆動開発では、テストを後から書くため、設計が曖昧なまま実装に移ることが多く、設計の改善やコードの品質向上が難しくなる可能性があります。
以上が、TDDの基本的な考え方やメリットについての紹介です。

FizzBuzzプログラムの実装

FizzBuzzの要件定義

ここからは、実際にTDDを使ってFizzBuzzプログラムを実装していきます。 FizzBuzzプログラムは、以下の要件を満たすプログラムです。

テストケースの洗い出し

TDDに従って早速Redのステップに入りたいところですが、まずはテストを行いやすくするために、設計を行う必要があります。 FizzBuzzプログラムは、現状の状態では、どのようなテストケースを書くべきか考えにくいです。細かく分解していきましょう。
分解する順序は最小テストから始めるのが一般的です。最小テストとは、最も単純なテストケースを指します。
  1. 1という数を入力した場合、「1」という文字列が出力される (最小テスト)
  1. 2という数を入力した場合、「2」という文字列が出力される
  1. 3という数を入力した場合、「Fizz」という文字列が出力される
  1. 5という数を入力した場合、「Buzz」という文字列が出力される
  1. 15という数を入力した場合、「FizzBuzz」という文字列が出力される
  1. 1~100までの数を入力した場合、上記のテストケースが全て通る

実装手順

0. 開発環境の準備

今回は、JavaScriptを使ってFizzBuzzプログラムを実装します。Jestというテストフレームワークを使ってテストを行います。Jestは、JavaScriptのテストフレームワークで、簡単にテストを書くことができます。他の技術については以下表にまとめておきます。
技術概要使用例
JavaScriptプログラムの実装に使用する言語です。FizzBuzzのロジックはJavaScriptで書かれています。関数はJavaScriptで書かれ、1から100までの数に対してFizzBuzzロジックを実行します。
JestJavaScriptのテストフレームワークです。テストケースを簡単に書けるようにします。関数を使用して、FizzBuzzの動作を検証するテストを実行します。

1. 1から始める最小テスト

最初のテストは、FizzBuzzの基本的な動作確認のため、最小値である「1」に焦点を当てます。
目的: 1から始めた場合、特に「Fizz」でも「Buzz」でもないため、そのまま「1」を返すことを確認します。
実装例:
このテストは、FizzBuzzの基本的な動作を確かめるための最初のステップで、条件に合うものがない場合、数そのものを返すことを確認します。

2. 3の倍数のケース

次に、3の倍数が与えられた場合に「Fizz」と出力されるかをテストします。3の倍数の場合、結果として「Fizz」が返されるべきです。
目的: 3の倍数を入力した際、「Fizz」という文字列が返されることを確認します。
実装例:
このテストケースは、3の倍数が適切に「Fizz」として処理されることを確認するためのテストです。

3. 5の倍数のケース

次に、5の倍数が入力された場合に「Buzz」と出力されるかどうかを確認します。5の倍数の場合、「Buzz」という文字列が返されるべきです。
目的: 5の倍数を入力した際、「Buzz」という文字列が返されることを確認します。
実装例:
このテストは、5の倍数が適切に「Buzz」として処理されることを確かめるためのものです。

4. 3と5の両方の倍数のケース

次に、3と5の両方の倍数である15が入力された場合に「FizzBuzz」と出力されるかをテストします。15は3と5両方の倍数なので、「FizzBuzz」という文字列が返されるべきです。
目的: 3と5の倍数である15の場合に、適切に「FizzBuzz」という文字列が返されるか確認します。
実装例:
このテストケースは、3と5両方の倍数の扱いが正しく行われるか確認するために必要です。

5. 15の倍数のケース

最後に、1から100までの全数値を繰り返して、それぞれが正しい結果を返すかをテストします。ここでは、上記で行った個別のテストを通じて、条件に従った結果が出力されることを確認します。
目的: 1から100までのすべての数について、条件に従って正しい文字列が出力されるかどうかを確認します。
実装例:
このテストは、1から100までのすべてのケースを一括で検証し、FizzBuzzのロジックが正しく動作することを確認します。

以上の5つの手順に従ってテストを実行することで、FizzBuzzの実装が期待通りに動作するかどうかをしっかりと確認できます。

ソースコードとリファクタリング

全てのテストコードに対して、それを満たすソースコードを以下に示します。
最初は数を文字列に変更して返すだけ。その後、3の倍数、5の倍数、3と5の両方の倍数の場合にそれぞれ「Fizz」「Buzz」「FizzBuzz」と返すように実装しています。それぞれ自動的にテストして、前Green(成功)した項目もデフレが起きていないことを確認します。
 
リファクタリングでは、変数名、重複した処理、計算ロジックなどを見直し、より明快に、わかりやすいコードになるように調整しました。 「毎回1箇所以上はリファクタする!」という意識を持っておくと、コードの品質が向上しやすくなるのでおすすめです!
 

まとめ

実際にやってみて、特にメリットに感じたのは、「テストの自動化」部分です。以前クリアしていたものがエラーになってしまう現象について、テストを書いていると、その原因がすぐにわかります。また、リファクタリングの際にも、テストがあることで、コードの品質を保ちながらリファクタリングを行うことができました。
また、最初にテストを書くことで、設計を考えることが重要となり、設計の改善やコードの品質向上が促進されることも感じました。
ぜひ、TDDを使って開発を進める際には、上記の手順を参考にしてみてください。読んでいただきありがとうございました!
 

参考文献