PHPUnit3で始めるユニットテスト

第3回 ショッピングカートクラスを作ってみる(2)

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

前回に引き続き,残りの仕様を実装していきましょう。

4番目の仕様「商品コードを指定して,カートに入っている数量を取得できる」

次は4番目の仕様

商品コードを指定して,カートに入っている数量を取得できる

です。ここでは,以下の3つのテストを考えてみました。

  • Cartオブジェクトを作成した直後は0を返す
  • 商品コード「001」を1個追加すると,getAmount('001')は1を返す
  • カートに入っていない商品が指定された場合,数量として0を返す

テストを追加したテストケースは次のようになりました。

<?php
require_once 'PHPUnit/Framework.php';
require_once 'Cart.php';

class CartTest extends PHPUnit_Framework_TestCase
{
    public function testInitCart() {
        $cart = new Cart();
        $this->assertTrue(is_array($cart->getItems()));
        $this->assertEquals(0, count($cart->getItems()));
    }

    public function testAdd() {
        $cart = new Cart();
        $this->assertTrue($cart->add('001', 1));
        $this->assertTrue($cart->add('001', 0));
        $this->assertTrue($cart->add('001', -1));
    }

    public function testAddNotNumeric() {
        $cart = new Cart();
        try {
            $cart->add('001', 'string');
        } catch (UnexpectedValueException $e) {
            return;
        }
        $this->fail();
    }

    public function testAddFloat() {
        $cart = new Cart();
        try {
            $cart->add('001', 1.5);
        } catch (UnexpectedValueException $e) {
            return;
        }
        $this->fail();
    }

    public function testGetAmount() {
        $cart = new Cart();
        $this->assertEquals(0, $cart->getAmount('001'));
        $cart->add('001', 1);
        $this->assertEquals(1, $cart->getAmount('001'));

        $this->assertEquals(0, $cart->getAmount('999'));
    }
}
<?php
class Cart
{
    public function getItems() {
        return array();
    }

    public function add($item_cd, $amount) {
        if (preg_match('/^-?\d+$/', $amount)) {
            return true;
        } else {
            throw new UnexpectedValueException('Invalid amount');
        }
    }

    public function getAmount($item_cd) {
    }
}
$ phpunit CartTest
PHPUnit 3.1.7 by Sebastian Bergmann.

....F

Time: 0 seconds

There was 1 failure:

1) testGetAmount(CartTest)
Failed asserting that <null> matches expected value <integer:0>.
/home/shimooka/public_html/gihyo.jp/01.phpunit/CartTest.php:43

FAILURES!
Tests: 5, Failures: 1.
$ 

さて,それではgetAmountメソッドを実装していきます。もうお気づきの方も多いと思いますが,現時点で商品情報をCartクラス内に保持する実装をしていません。テスト内容も貯まってきていますので,そろそろ本格的に実装し始めても良い頃でしょう。

まず,1番目の仕様に対するテストを実装した際,Cartクラス内の商品情報は,キーに商品コード,値に数量を持つハッシュでとすることにしましたので,それを実装しましょう。また,addメソッドも併せて修正しておきます。修正したCartクラスは以下のようになりました。

<?php
class Cart
{
    private $items;

    public function __construct() {
        $this->items = array();
    }

    public function getItems() {
        return array();
    }

    public function add($item_cd, $amount) {
        if (preg_match('/^-?\d+$/', $amount)) {
            if (!isset($this->items[$item_cd])) {
                $this->items[$item_cd] = 0;
            }
            $this->items[$item_cd] += (int)$amount;
            return true;
        } else {
            throw new UnexpectedValueException('Invalid amount');
        }
    }

    public function getAmount($item_cd) {
        if (isset($this->items[$item_cd])) {
            return $this->items[$item_cd];
        } else {
            return 0;
        }
    }
}

テストを実行してみましょう。

$ phpunit CartTest
PHPUnit 3.1.7 by Sebastian Bergmann.

.....

Time: 0 seconds


OK (5 tests)
$ 

今までのテストを含め,全てテストをパスしています。しかし,1つの商品コードに対してのテストしか行っていません。複数の商品の場合の不具合はないのでしょうか?それもテストしてみましょう。以下のテストケースは,1つのカートに複数の商品を追加・数量変更し,それぞれの個数が正しく返ってくるかどうかのテストを追加しています。

<?php
require_once 'PHPUnit/Framework.php';
require_once 'Cart.php';

class CartTest extends PHPUnit_Framework_TestCase
{
    public function testInitCart() {
        $cart = new Cart();
        $this->assertTrue(is_array($cart->getItems()));
        $this->assertEquals(0, count($cart->getItems()));
    }

    public function testAdd() {
        $cart = new Cart();
        $this->assertTrue($cart->add('001', 1));
        $this->assertTrue($cart->add('001', 0));
        $this->assertTrue($cart->add('001', -1));
    }

    public function testAddNotNumeric() {
        $cart = new Cart();
        try {
            $cart->add('001', 'string');
        } catch (UnexpectedValueException $e) {
            return;
        }
        $this->fail();
    }

    public function testAddFloat() {
        $cart = new Cart();
        try {
            $cart->add('001', 1.5);
        } catch (UnexpectedValueException $e) {
            return;
        }
        $this->fail();
    }

    public function testGetAmount() {
        $cart = new Cart();
        $this->assertEquals(0, $cart->getAmount('001'));
        $cart->add('001', 1);
        $this->assertEquals(1, $cart->getAmount('001'));

        $this->assertEquals(0, $cart->getAmount('999'));

        $cart->add('002', 1);
        $this->assertEquals(1, $cart->getAmount('001'));
        $this->assertEquals(1, $cart->getAmount('002'));

        $cart->add('001', -1);
        $this->assertEquals(0, $cart->getAmount('001'));
        $this->assertEquals(1, $cart->getAmount('002'));
    }
}
$ phpunit CartTest
PHPUnit 3.1.7 by Sebastian Bergmann.

.....

Time: 0 seconds


OK (5 tests)
$ 

大丈夫そうです。これで4番目の仕様も実装完了とします。

著者プロフィール

下岡秀幸(しもおかひでゆき)

PHP関連の情報サイト「Do You PHP?」の管理人。PHP歴は長いが,あまり仕事で使ったことがないという噂がある。最近はα版のPEAR・PECLに手を出しては地雷を踏んでいることが多い。

URLhttp://www.doyouphp.jp/http://d.hatena.ne.jp/shimooka/

コメント

  • 結果が違うと思う。

    testAddUpperLimit()
    は『.』じゃなくて『F』になる。

    Commented : #1  オガーン (2014/05/07, 23:58)

コメントの記入