測試跟隨程式改變

除非是只用一次的程式,否則任何程式都有機會需要被維護,而維護就表示我們需要在程式上新增功能或進行修改。然而在新增或修改時,因為邏輯的改變就會需要調整測試。

Cart 類別加入運費計算

接下來我們要為 Cart.php 加入一個運費計算,當總金額未滿 $500 時,要加入 $20 的運費;另外我們把運費也當做是其中一個商品,它的價格是 $20 ,這在實務上是很常見的做法。

既然運費是其中一項商品,我們就把它加到 Cart::$prdoucts 裡,並設定一個運費鍵值常數 FREIGHT_KEY

private $products = [
    // ... 加入運費商品
    [
        'name'     => '運費',
        'quantity' => 0,
        'price'    => 20,
        'subtotal' => 0,
    ],
];

CONST FREIGHT_KEY = 6;

修改測試

然後我們修改 CartTest.phpprovider 方法,讓總計未滿 $500 的測試要包含運費金額。

public function provider()
{
    return [
        [ [ 1, 0, 0, 0, 0, 0 ], 199 + 20 ],
        [ [ 1, 0, 0, 2, 0, 0 ], 797 ],
    ];
}

商品數量改為 7 個:

public function testGetProducts()
{
    // ...
    $this->assertEquals(7, count($products));
    // ...
}

執行測試:

C:\project> phpunit
PHPUnit 4.2.6 by Sebastian Bergmann.

Configuration read from C:\project\phpunit.xml

F.....

Time: 537 ms, Memory: 8.00Mb

There was 1 failure:

1) CartTest::testUpdateQuantitiesAndGetTotal with data set #0 (array(1, 0, 0, 0,
 0, 0), 219)
Failed asserting that 199 matches expected 219.

C:\project\tests\CartTest.php:27

FAILURES!
Tests: 6, Assertions: 7, Failures: 1.

可以看到測試不通過,因為我們還沒加入運費判斷。

加入運費判斷

再來看原來的 updateQuantities 方法:

public function updateQuantities($quantities)
{
    // 更新商品數量並算出小計
    foreach ($quantities as $key => $qty) {
        if (!is_numeric($qty) || (int) $qty < 0) {
            throw new CartException("數量不正確,請輸入 0 或 0 以上的整數", 1);
        }

        $this->products[$key]['quantity'] = $qty;
        $this->products[$key]['subtotal'] =
            $this->products[$key]['quantity'] *
            $this->products[$key]['price'];
    }

    // 計算總金額
    $this->total = 0;
    foreach ($this->products as $key => $product) {
        $this->total += $product['subtotal'];
    }
}

我們在計算總金額的後面加上一段運費的判斷:

    // 運費
    if ($this->total < 500) {
        $this->products[self::FREIGHT_KEY]['quantity'] = 1;
        $this->products[self::FREIGHT_KEY]['subtotal'] =
            $this->products[self::FREIGHT_KEY]['quantity'] *
            $this->products[self::FREIGHT_KEY]['price'];

        // 加上運費
        $this->total += $this->products[FREIGHT_KEY]['subtotal'];
    } else {
        $this->products[self::FREIGHT_KEY]['quantity'] = 0;
        $this->products[self::FREIGHT_KEY]['subtotal'] =
            $this->products[self::FREIGHT_KEY]['quantity'] *
            $this->products[self::FREIGHT_KEY]['price'];
    }

別忘了在 view.php 裡把運費的輸入欄位隱藏起來:

<?php if ($key !== Cart::FREIGHT_KEY): ?>
<input type="text" name="quantity[<?= $key ?>]" class="form-control" value="<?= $product['quantity'] ?>">
<?php else: ?>
<input type="hidden" name="quantity[<?= $key ?>]" value="0">
<?php endif; ?>

再執行一次測試:

C:\project> phpunit
PHPUnit 4.2.6 by Sebastian Bergmann.

Configuration read from C:\project\phpunit.xml

E.....

Time: 551 ms, Memory: 7.75Mb

There was 1 error:

1) CartTest::testUpdateQuantitiesAndGetTotal with data set #0 (array(1, 0, 0, 0,
 0, 0), 219)
Use of undefined constant FREIGHT_KEY - assumed 'FREIGHT_KEY'

C:\project\Cart.php:92
C:\project\tests\CartTest.php:26

FAILURES!
Tests: 6, Assertions: 6, Errors: 1.

發現還是有錯,但 PHPUnit 幫我們指出在 Cart.php 第 92 行的 FREIGHT_KEY 有錯誤,因為我們沒有加上 self:: 。修正後再執行一次,測試就通過了。

練習

  • 思考看看有沒有更好的做法來加入運費?

  • 思考看看有無沒測試到的狀況?