k2.craft
【TDD初心者】テストが好きなだけの実務未経験エンジニアがTDDを理解する

【TDD初心者】テストが好きなだけの実務未経験エンジニアがTDDを理解する

はじめ

ソフトウェアテストが大好きな24卒エンジニアです。先日読んだ以下の書籍

ちょうぜつソフトウェア設計入門 PHPで理解するオブジェクト指向の活用/田中ひさてる【3000円以上送料無料】

created by Rinker

こちらでTDDことテスト駆動開発がどんなものか理解できたので今回はそれを整理します。実際にPHPのコードを使って、どんなものか体験してみようと思います。

テスト駆動開発の位置付け

TDD(テスト駆動開発)は、「開発」の技法のことを指します。一方で単体テストは、「テスト」の技法のことを指します。

例えば、このコード…

function saturate(int $value, int $minValue, int $maxValue):int
{
   return min(max($value, $minValue), $maxValue);
}

もし、saturate関数が動作不具合になったとき

  • saturateのコードの記述にバグがあるか
  • max or min関数のどちらかにバグがあるか

この2つのどちらかを疑います。ソフトウェアの不具合は、幅広い下位構造の組み合わせでできています。

テスト駆動開発のメリット・デメリット

メリット

たくさん考えば出ると思いますが、2つ上げます

バグを早急に発見できる

  • バグがある段階で発見できるため、後の負担が軽減されます。手戻りが少ないことは非常に良いですね

要件や仕様を理解しやすくなる

  • テストコードを記述するとき、要件を理解しないと書くことが困難です。テストをはじめに書くことで、仕様ってこんな感じなのか!という理解ができます

デメリット

ここでも2つ例を挙げて考えます

慣れるのに時間が….

  • 先に実装しちゃうぜ!という感じで開発してきた人にとって習得するのに時間がかかります。初期段階がめちゃくちゃ大変です

実装・保守に時間がかかる

  • 長期的な目線を考慮すると保守に時間がかかります。修正作業にも時間がかかる可能性があるので考えておきたいですね

テスト駆動開発をするための準備をする

phpのパッケージマネージャComposerを使って、PHPUnitをインストールします。

$ composer require --dev phpunit/phpunit

コマンドを入力すると、vendor/binディレクトリにphpunitコマンドが追加されます。

※ 本、記事ではテスト駆動開発がどういうものなのかに触れるため、具体的なインストール手順は割愛します。

簡単なテストコードを書いてみる

最初にメソッドを足すのではなく、最も簡単なテストケースを書きます。

1.メソッドを足すのではなく、簡単なテストケースを記述していきます

namespace FizzBuzz\Core;
use PHPUnit\Framework\TestCase;

class NumberConverterTest extends TestCase
{
    public function testConvert(): void
    {
        $fizzbuzz = new NumberConverter();
        $this->assertEquals("1", fizzBuzz->convert(1)); 
    }
}

2. 次に、最低限言語文法レベルのエラーを起こさないように、とりあえず呼び出されるメソッドを追加します

class NumberConverter
{
 // 引数の型と値はこんな感じになりそうかなくらいで記述します
  public function convert(int $n): string
  {
    return ""
  }
}

3. テストをパスするだけの実装を行います。今回は固定で文字を返してしまいます。

class NumberConverter
{
  public function convert(int $n): string
  {
    return "1" // "1"かどうかかを知りたい!
  }
}

4. 他のシナリオも考えてみます

namespace FizzBuzz\Core;
use PHPUnit\Framework\TestCase;

class NumberConverterTest extends TestCase
{
    public function testConvert(): void
    {
        $fizzbuzz = new NumberConverter();
        $this->assertEquals("1", fizzBuzz->convert(1));
        $this->assertEquals("2", fizzBuzz->convert(2)); // 追加した
    }
}

5. テストがパスできる最小限の実装を記述していきます。短く記述していきましょう

class NumberConverter
{
  public function convert(int $n): string
  {
    return (string)$n;
  }
}

6. 次に3がやってきた時に、Fizzを返せるようにしていきます

class NumberConverterTest extends TestCase
{
    public function testConvert(): void
    {
        $fizzbuzz = new NumberConverter();
        $this->assertEquals("1", fizzBuzz->convert(1));
        $this->assertEquals("2", fizzBuzz->convert(2));
        $this->assertEquals("Fizz", fizzBuzz->convert(3)); // 追加した
    }
}

7. ロジックを組んでいきます

class NumberConverter
{
     public function convert(int $n): string
     {
         if($n == 3){
           return "Fizz";

         } else {
           return (string)$n;
         
      }
     }
}

上記のような流れで、少しずつコードを記述して進めていきます。

テストファースト

  • テストを追加→失敗確認→最小限の実装→成功

このサイクルを繰り返すのがテスト駆動開発の流れです。

kei

kei

小さく、小さくコードを書いていく開発手法は個人的に好きです。エラーも起こりにくそう

実装よりも、テストを最初に書いていくのがテスト駆動開発の鉄則のようです。

おわりに

PHPを例に取り上げ、テスト駆動開発がどういうものかを見ていきました。

テストファーストでコードを少しずつ記述していく開発は、チームでも個人でも進めやすい気がします。ただ慣れるのには時間がかかりそうだなとも思いました。

参考