こんにちは!伊神です!

今回はテスト駆動開発(Test-Driven Development: TDD)について記事にします!

テストを優先しながら実装を進める開発手法で、早期にバグを発見することができたり必要な機能を無駄なく開発することができます!

目次

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

テスト駆動開発(TDD)とは、テストファーストな開発手法です。

はじめにテストコードから記述してき、次にそのテストを通過するようなプログラムを作り、最後にテストコードが成功する前提でリファクタリングを進めるということを繰り返し行います。

開発サイクル

テスト駆動開発のサイクルを大きく分けると3つのステップになります。

  • Red:テストの失敗
  • Green:テストの成功
  • Refactor:リファクタリングの実施

という順序で進められます。

※Red, Greenはテストツールによるテスト失敗が赤色、成功が緑色の表示になることからこう呼ばれています。

具体的なサイクルは下記になります。

  1. 作成したいコード目標を考える
  2. 目標を示すテストを書く
  3. 2.で書いたテストを実行して失敗させる(Red)
  4. 目的のコードを書く
  5. 2で書いたテストを通す(Green)
  6. テストが成功するのは前提条件でリファクタリングを行う(Refactor)

tdd

(例)FizzBuzz問題を解く際の書き方

目標を考えてコードを作成するためそれぞれの目標を細分化し、To-Doリストを作成する。

目標の決め手は下記2つ。

  1. 最も重要なものを最初に実施
  2. テスト容易性が高いものから実施

まずはTo-Doリストにしてみると下記のようになる。

要件

- 1〜100の数字を入力とする
- 数字が3の倍数なら「Fizz」と表示させる
- 数字が5の倍数なら「Buzz」と表示させる
- 数字が3と5の両方の倍数なら「FizzBuzz」と表示させる

ここから細分化しテスト容易性が低いものを省くためにTo-Doとして一覧で書き出してみる。

細分化の流れは省略しますが最終的には下記のようにします。

テスト容易性:高 重要度:高
- [ ] 数字を文字列に変換する
  - [ ] 1を渡すと文字列1を返す
  - [ ] 2を渡すと文字列2を返す

- [ ] 3の倍数のときは数の代わりに「Fizz」に変換する
  - [ ] 3を渡すと文字列"Fizz"を返す

- [ ] 5の倍数のときは数の代わりに「Buzz」に変換する
  - [ ] 5を渡すと文字列"Buzz"を返す

- [ ] 3と5両方の倍数のときは数の代わりに「FizzBuzz」に変換する

テスト容易性:低 重要度:低
- [ ] 1から100まで
- [ ] 表示する

こうすることによってテストコードを書きやすくする。

ここで「数字を文字列に変換するテスト」を2回行っているのは初めてのテストのため。

このことを三角測量といい想定と違う動きをしないか確認しておきます。

テストの書き方

基本は、「準備」「実行」「検証」のコードを書きます。

今回は、開発サイクルの3つの部分と最初のステップのみを記載します。

テストの失敗(Red)

文字列に変換せずにわざと失敗させる

src/FizzBuzz.php

public function _1を渡すと1を返す($num)
{
    return $num;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->_1を渡すと1を返す(1));

テストの成功(Green)

文字列に変換する処理を加えテストを成功させる

src/FizzBuzz.php

public function _1を渡すと文字列1を返す(int $num)
{
    $numStr = (string)$num;
    return $numStr;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->_1を渡すと文字列1を返す(1));

リファクタリング(Refactor)

src/FizzBuzz.php の関数名を「_1を渡すと文字列1を返す」から「convert」にリファクタリングし変更する。

src/FizzBuzz.php

public function convert(int $num)
{
     $numStr = (string)$num;
     return $numStr;
}

test/FizzBuzzTest.php

// 準備
$fizzbuzz = new FizzBuzz();
// 実行
// 検証
$this->assertSame('1', $fizzbuzz->convert(1));

今回は、関数名のリファクタリングでしたが、コードが複雑になった際コードの綺麗さやみやすさなどテストが成功する前提でリファクタリングを行います。

TDDを実践するメリット/デメリット

メリット

  • バグや手戻りが起きにくい

上記の開発サイクルにもあったように細かくサイクルを繰り返しているためバグを早期に検知・修正を行うことができます。

  • テスト対象のクラスの変更を助けることができる

テスト対象のクラスが変更されたことをテストコードが感知して、開発者に通知してくれます。

デメリット

  • 時間がかかる

実装しながらテストを書くためコーディングに時間がかかってしまいます。

  • テストが難しい場合がある

データベースやGUIなどテストが難しい場合もあり、モックやe2eテストというものが必要になることがあります。

参考

TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング

テスト駆動開発

まとめ

今回は、テスト駆動開発(TDD)をご紹介させて頂きました。

開発に時間がかかってしまったり、慣れるまでに大変だったりとデメリットもありますが早期にバグを発見することができたり必要な機能を無駄なく開発できたりとメリットがたくさんあります!

他の開発手法にビヘイビア駆動開発(BDD)や受け入れテスト駆動開発(ATDD)などがあったりするので、随時調べ学習していきたいと思います!