你的浏览器还没开启 Javascript 功能!

【読書メモ】The Art Of Unit Testing 2nd Edition - Chap1

背景

ここ何年、アジャイル開発を導入しようとSIer企業がどんどん増えていますが、成功事例・実績がまだまだ目立たないのも事実です。

成功事例が少ない原因は企業文化が合わないなども考えられますが、個人的に一番の原因はアジャイル脳へシフトできていないかと思います。

技術基盤がまだ整っていない状態で、既存の開発手法・体制を無理やりアジャイルに嵌め込もうとすると破綻するのも納得できます。

アジャイル開発を行う上で自動化は必要不可欠のため、単体テストについてイチから勉強し直すという意味で、The Art of Unit Testing: with examples in C#の読書メモを残していきたいと思います。

単体テストの定義

  • 単体テスト : プログラム構成の最小のユニット(単位)を呼び出し、そのユニットの呼び出し結果が想定した結果と一致するかを検証するテスト
  • SUT : System Under Test、テスト対象のプログラムのことを指す

単体テストを始める際に、一番難しいことはどういうのが良い単体テストであるかを定義することです。

もし、単体テストの定義をわからないまま適当にテストコードを書くくらいなら、書かないほうがマシです。そうしたほうが少なくとも出来の悪いテストコードをメンテナンスする時間を節約できます。

良い単体テストの特性

単体テストは以下の特性を備わっているはず。

  • 自動化されており、何度も実行できる
  • 簡単に実装できる
  • 二日目になっても存在価値がある(臨時的ではない)
  • 誰でも実行できる
  • 実行速度が早い
  • 何度実行しても、実行結果が同じ(プロダクトコードが変わらない限り)
  • テスト対象のユニットを完全にコントロールすることができる
  • 完全に分離されている
  • 実行結果が失敗なら、「期待値は何か」、「問題点はどこか」などの情報がはっきりわかる

それを踏まえて、自分が今まで行った”単体テスト”を思い返し、以下の質問に答えてみてください。

  • 2ヶ月前に書いた単体テストは今日実行しても当時と同じ結果得られるか?
  • 2ヶ月前に書いた単体テストは、チームメンバーの誰が実行しても結果は一緒か?
  • 数分で単体テストを実行し終えることができるか?
  • ボタン一つで全単体テストを実行できるか?
  • 数分で単体テストを実装することが可能か?

上記の質問に1つでもNoがあるなら、それは単体テストではないことを意味し、今まで単体テストをやっているつもりが、実は結合テストであることが多い。

結合テスト

テストに本物のファイルやデータベースが必要ならそれは結合テスト領域をテストすることを意味しています。

結合テスト自体は悪いことではありません。単体テスト同じくらい重要ですが、ただし、この種のテストは本物のデータを扱うため、実行痕跡が消すことが難しく、完全にコントロールすることが難しい。

また、一回にテストすべきものが多すぎることも問題です。

車が壊れた場合、部品を一つ一つに問題ないことを確認できて初めて、部品同士の相互作用に問題ないかを確認するものです。

デモ

テストフレームワークを使わなくても、単体テストを実装することが可能です。

動作環境

環境/ソフト内容
OSWindows10 1903
.NET Core SDK3.0.100
言語C#
IDEVisual Studio 2019

SUT

テスト対象プログラム作成します。

using System;

public class SimpleParser{

    // 文字列を数値に型変換
    public int ParseAndSum(string numbers){

        // 文字数が0なら0
        if (numbers.Length == 0)
        {
            return 0;
        }

        // ","混在する場合エラー
        if (!numbers.Contains(","))
        {
            return int.Parse(numbers);
        }else{
            throw new InvalidOperationException("I can only handle 0 or 1 numbers for now!");
        }

    }
}

単体テスト

テスト対象をテストするプログラムを作成します。

using System;

public class SimpleParserTests
{
    // 空白文字列を渡し、0以外ならテスト失敗
    public static void TestReturnZeroWhenEmptyString()
    {
        try
        {
            var p = new SimpleParser();
            int result = p.ParseAndSum(string.Empty);
            if (result != 0)
            {
                Console.WriteLine(
                    @"***SimpleParserTests.TestReturnsZeroWhenEmptyString: 
-------
Parse and sum should have returned 0 on an empty string"
                );
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }
    }
}

単体テスト呼び出しプログラム

using System;

namespace chapter01
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // 単体テスト実行
                SimpleParserTests.TestReturnZeroWhenEmptyString();
            }
            catch (Exception ex)
            {
               Console.WriteLine(ex);
            }
        }
    }
}

テストフレームワークを使えば補助機能を利用でき、もっと単体テスト実装しやすくなります。

単体テストをどうやって書くのかをわかったところで、単体テストはいつ書くべきかについて考えてみます。

テスト駆動開発

テスト駆動開発(TDD)はプロダクトコードを書くに、テストコードを書くことがベストプラクティスと信じる開発手法です。

  • TDDの流れ

TDD-Flow

TDDは開発において有意義ですが、その対価として学習コスト(学習時間、実践練習、良いコーディングスタイル)が高い。ですが、その学習コストは結果的に価値のあるものであることは間違いない。

まとめ

良い単体テストの定義は

  • 自動化されたプログラムである。他のプログラムを呼び出すことで、テスト対象コードのロジックを検証する
  • 自動化テストフレームワークで実装されたテストコード
  • 実装が容易
  • 実行速度が早い
  • 開発チームメンバーが誰が実行しても同じ結果を得られる

出典

The Art of Unit Testing: with examples in C#

コード

Github Repository