Fixtures (基礎設施)

通常在測試裡面,我們會需要初始化我們的測試環境 (例如建立一個被測試類別的物件) ,但如果每一個測試都要做一次初始化的動作,就會非常麻煩。

雖然可以把初始化的動作包裝成一個方法,但還是需要每次都在測試一開始時手動呼叫。如果這個動作可以讓自動測試幫我們處理,就會方便很多。

setUp 方法與 tearDown 方法

PHPUnit 提供了一組協助我們初始化測試環境的方法,即 setUp 方法與 tearDown 方法。而在 setUp 方法 初始化,以供所有測試共用的物件或變數,則稱為 fixture 。

setUp 方法中,會將 fixture 初始化,讓每個測試都可以使用 fixture 最初的狀態。而在 tearDown 方法中,一般是會將 fixture 消滅,以釋出它所佔用的記憶體,否則就得靠不確定何時會執行的 GC (垃圾收集) 。

修改測試

以下程式為引入 fixture 後的寫法:

<?php
/* tests/CartTest.php */

require __DIR__ . '/../Cart.php';

class CartTest extends PHPUnit_Framework_TestCase
{
    private $cart = null;

    public function setUp()
    {
        $this->cart = new Cart();
    }

    public function tearDown()
    {
        $this->cart = null;
    }

    /**
     * @dataProvider provider
     * @group update
     */
    public function testUpdateQuantitiesAndGetTotal($quantities, $expected)
    {
        $this->cart->updateQuantities($quantities);
        $this->assertEquals($expected, $this->cart->getTotal());
    }

    // ...

    /**
     * @group update
     * @group exception
     */
    public function testUpdateQuantitiesWithException()
    {
        $this->setExpectedException('CartException');
        $quantities = [ -1, 0, 0, 0, 0, 0 ];
        $this->cart->updateQuantities($quantities); // 預期會產生一個 Exception
    }

    /**
     * @group get
     */
    public function testGetProducts()
    {
        $products = $this->cart->getProducts();
        $this->assertEquals(6, count($products));
        $this->assertEquals(0, $products[3]['quantity']);
    }
}

主要改變有:

  • 加入一個 fixture ,即私有屬性 $cart

  • 加入 setUptearDown 兩個方法; setUp 方法負責初始化 fixture ,將它設定為 Cart 類別的物件實體;而 tearDown 方法則清除 fixture 所佔用的記憶體。

  • 將原有測試中的 $cart = new Cart(); 移除,並將 $cart 改為 $this->cart

執行順序

每個測試方法執行之前,都會先執行 setUp 方法;而結束之後,則會執行 tearDown 方法。以此例來說,順序如下:

  1. setUp
  2. testUpdateQuantitiesAndGetTotal
  3. tearDown
  4. setUp
  5. testUpdateQuantitiesAndGetTotal
  6. tearDown
  7. setUp
  8. testUpdateQuantitiesWithException
  9. tearDown
  10. setUp
  11. testGetProducts
  12. tearDown

官方手冊參考

練習

  • 解釋為什麼 $this->cart 可以在每個測試中使用?

  • 思考看看為什麼每次執行測試前,都要重新初始化 fixture ?