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

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

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

不足している仕様とテストを追加する

さて,挙げられた仕様はすべて実装しましたが,ここで不足している仕様やテストがないかどうかを確認してみましょう。まず,カートに入っている商品の数量に制限がありません。数量が負数になることはないと考えられますので,下限は0としましょう。一方,数量の上限はシステムの要件で異なることが多いですので,ちょっと非現実的ですが,ここではPHP_INT_MAX(2147483647)としておきましょう。また,数量が上限・下限を越えた場合の処理はどうでしょうか?ここでは,上限を超えた場合はSPL拡張モジュールで定義されているOutOfRangeExceptionを投げ,下限を越えた場合はエラーは発生せず,数量が0になることにしましょう。まとめると,以下の3つを新しい仕様として追加します。

  • カートに入っている商品の数量は0以上PHP_INT_MAX以下
  • 数量の上限を超過した場合,OutOfRangeExceptionを投げる
  • 数量の下限を超過した場合,数量は0になる

現在の仕様では「数量が0」である商品もカートの中に存在することができてしまいます。この場合,カートから商品を取り除いた方が良いと考えられますので,これも仕様として追加しておきましょう。これにより,上の3つ目の仕様は

  • 数量が下限もしくは下限を超過した場合,その商品はカートから取り除かれる

となります。

それでは,新しく挙げた仕様を実装します。前者2つはaddメソッドのテストとして,3つ目はgetItemsメソッドのテストとして,それぞれテストケースに追加することにします。

<?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'));
    }

    public function testGetItems() {
        $cart = new Cart();
        $cart->add('001', 3);
        $this->assertEquals(1, count($cart->getItems()));
        $cart->add('002', 2);
        $this->assertEquals(2, count($cart->getItems()));

        $items = $cart->getItems();
        $this->assertEquals(3, $items['001']);
        $this->assertEquals(2, $items['002']);
    }

    public function testClearCart() {
        $cart = new Cart();
        $cart->add('001', 1);
        $cart->add('002', 2);
        $cart->add('003', 3);
        $cart->clear();
        $this->assertTrue(is_array($cart->getItems()));
        $this->assertEquals(0, count($cart->getItems()));
    }

    public function testAddUpperLimit() {
        $cart = new Cart();
        $cart->add('001', PHP_INT_MAX);
        try {
            $cart->add('001', 1);
        } catch (OutOfRangeException $e) {
            return;
        }
        $this->fail();
    }

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

        $cart->clear();
        $cart->add('001', 1);
        $this->assertEquals(1, count($cart->getItems()));
        $cart->add('001', -2);
        $this->assertEquals(0, count($cart->getItems()));
    }

    public function testAddRemove() {
        $cart = new Cart();
        $cart->add('001', 2);
        $cart->add('002', 3);
        $this->assertEquals(2, count($cart->getItems()));

        $cart->add('001', -2);
        $items = $cart->getItems();
        $this->assertEquals(1, count($items));
        $this->assertFalse(isset($items['001']));
        $this->assertEquals(3, $items['002']);

        $cart->add('002', -3);
        $items = $cart->getItems();
        $this->assertEquals(0, count($items));
        $this->assertFalse(isset($items['001']));
        $this->assertFalse(isset($items['002']));
    }
}
$ phpunit CartTest
  PHPUnit 3.1.7 by Sebastian Bergmann.

.......FFF

Time: 0 seconds

There were 3 failures:

1) testAddUpperLimit(CartTest)
/home/shimooka/public_html/gihyo.jp/CartTest.php:87

2) testAddUnderLimit(CartTest)
Failed asserting that <integer:1> matches expected value <integer:0>.
/home/shimooka/public_html/gihyo.jp/CartTest.php:95

3) testAddRemove(CartTest)
Failed asserting that <integer:2> matches expected value <integer:1>.
/home/shimooka/public_html/gihyo.jp/CartTest.php:112

FAILURES!
Tests: 10, Failures: 3.
$ 

テストが通るように,Cartクラスを実装していきます。

<?php
class Cart
{
    private $items;

    public function __construct() {
        $this->clear();
    }

    public function getItems() {
        return $this->items;
    }

    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;
            if ($this->items[$item_cd] > PHP_INT_MAX) {
                throw new OutOfRangeException('the amount exceeded PHP_INT_MAX');
            }
            if ($this->items[$item_cd] <= 0) {
                unset($this->items[$item_cd]);
            }
            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;
        }
    }

    public function clear() {
        $this->items = array();
    }
}
$ phpunit CartTest
PHP Parse error:  syntax error, unexpected T_PUBLIC in /home/shimooka/public_ht ml/gihyo.jp/Cart.php on line 29

Parse error: syntax error, unexpected T_PUBLIC in /home/shimooka/public_html/gi hyo.jp/Cart.php on line 29
[shimooka@centos gihyo.jp]$ phpunit CartTest
PHPUnit 3.1.7 by Sebastian Bergmann.

..........

Time: 0 seconds


OK (10 tests)
$ 

うまくいきました。

著者プロフィール

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

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

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