テストを“いちばん重要な財産”と考えると見えるもの

第2回 本当にコードを失っても大丈夫なのか,確かめてみよう

この記事を読むのに必要な時間:およそ 8 分

本来のrailクラスにもgethttp()メソッドを作り,こちらはオリジナル通りにfile_get_contents()を呼ぶようにしておきます。

        function        gethttp($url) {
                return file_get_contents($url);
        }

では,このgethttp()メソッドを使って,drive()メソッドを書いていきます。例によって,1つだけです。テストが失敗することを確認します(リスト6)。

リスト6 drive()のテスト

#! /usr/local/bin/php
<?php
$url = "http://localhost/gh/rail/simrail.php5";

class   rail {
        var     $number;
        function        rail($number = "00") {
                $this->number = $number;
        }
        function        gethttp($url) {
                return file_get_contents($url);
        }
}


class   testrail extends rail {
        var     $gethttpurl;
        function        gethttp($url) {
                $this->gethttpurl = $url;
        }
        function        test1() {
                $obj =& new testrail();
                asserteq("00", $obj->number);
                $obj =& new testrail("12");
                asserteq("12", $obj->number);
                
                $obj->gethttp("http://a.b/");
                asserteq("http://a.b/", $obj->gethttpurl);
                
                $obj =& new testrail("34");
                $obj->drive();
                asserteq("http://a.b/?34=A", $obj->gethttpurl);
                
        }
}


if (function_exists("asserteq"))
        return;


#
# |---r4---|---r3---|---r2---|---r1---|---r0---|
#

$r0 =& new rail("00");

〈以下省略〉

コード本体も,まずはシンプルな方法でテストを通します。

        function        drive() {
                $this->gethttp("http://a.b/?34=A");
        }

次のテストでは,コンストラクタに渡していたパラメータを変えます。

                $obj =& new testrail("56");
                $obj->drive();
                asserteq("http://a.b/?56=A", $obj->gethttpurl);

そしてコードでは,パラメータを参照するように修正します。

        function        drive($speed = 0) {
                $this->gethttp("http://a.b/?{$this->number}=A");
        }

今度はメソッドのパラメータです。これも,最初は値のチェックをしません。

                $obj->drive(1);
                asserteq("http://a.b/?56=B", $obj->gethttpurl);

コードが追いかけます。

        function        drive($speed = 0) {
                $command = substr("ABCDEF", $speed, 1);
                $this->gethttp("{$this->baseurl}?{$this->number}={$command}");
        }

ところで,オリジナル(リスト2)ではグローバル変数の$urlを参照していたのですが,これは必要に応じて書き変わると考えられるため,テスト用に使うのは好ましくありません。そこで,メンバ変数にURLを保存しておき,コンストラクタ内でグローバル変数からコピーすることにしました。テスト用クラスのコンストラクタでは,これを上書きするわけです。

この作業については,失敗するテストを書けないですが,これまでのテストが今までと変わらずに通ることは自動的にテストされます。というわけでリスト7になりました。ついでに,さきほどのdrive()に,範囲ギリギリのテストも追加してあります。

リスト7 リファクタリングの後

#! /usr/local/bin/php
<?php
$url = "http://localhost/gh/rail/simrail.php5";

class   rail {
        var     $baseurl;
        var     $number;
        function        rail($number = "00") {
                global  $url;
                
                $this->baseurl = $url;
                $this->number = $number;
        }
        function        gethttp($url) {
                return file_get_contents($url);
        }
        function        drive($speed = 0) {
                $command = substr("ABCDEF", $speed, 1);
                $this->gethttp("{$this->baseurl}?{$this->number}={$command}");
        }
}


class   testrail extends rail {
        var     $gethttpurl;
        function        testrail($number = "00") {
                parent::rail($number);
                $this->baseurl = "http://a.b/";
        }
        function        gethttp($url) {
                $this->gethttpurl = $url;
        }
        function        test1() {
                $obj =& new testrail();
                asserteq("00", $obj->number);
                $obj =& new testrail("12");
                asserteq("12", $obj->number);
                
                $obj->gethttp("http://a.b/");
                asserteq("http://a.b/", $obj->gethttpurl);
                
                $obj =& new testrail("34");
                $obj->drive();
                asserteq("http://a.b/?34=A", $obj->gethttpurl);
                
                $obj =& new testrail("56");
                $obj->drive();
                asserteq("http://a.b/?56=A", $obj->gethttpurl);
                
                $obj->drive(0);
                asserteq("http://a.b/?56=A", $obj->gethttpurl);
                
                $obj->drive(1);
                asserteq("http://a.b/?56=B", $obj->gethttpurl);
                
                $obj->drive(5);
                asserteq("http://a.b/?56=F", $obj->gethttpurl);
                
        }
}


if (function_exists("asserteq"))
        return;


#
# |---r4---|---r3---|---r2---|---r1---|---r0---|
#

$r0 =& new rail("00");

〈以下省略〉

続いて,範囲外の値のテストを追加します。

                $obj->drive(6);
                asserteq("http://a.b/?56=F", $obj->gethttpurl);

そして,コードでは範囲チェックを追加します。

        function        drive($speed = 0) {
                $command = substr("ABCDEF", min($speed, 5), 1);
                $this->gethttp("{$this->baseurl}?{$this->number}={$command}");
        }

この続きは宿題にするとして,せっかくですから,ここまでの流れを振り返ってみましょう。

著者プロフィール

木元峰之(きもとみねゆき)

独立系ソフトハウスに8年間勤務,パッケージソフトの開発や記事執筆などを行う。現在はフリーのコンサルタント。SWESTなどのワークショップで分科会のコーディネータを務める。デジタル回路設計歴30年,プログラミング歴27年。

きもと特急電子設計
URL:http://business.pa-i.org/