導入驗收測試(一)

導入 Composer 的時候,我們有新增一個範例的單元測試。如果可以的話,下一步當然就是開始寫一些基本的單元測試,來保護系統元件。但,並不是每個專案都能這麼開心,如果真的無法寫單元測試的話該怎麼辦呢?沒關係,我們還能寫驗收測試!

HTTP Tests

Laravel 的測試套件相當完整,HTTP Tests 是 Laravel 測 HTML 的說明文件。

我們先試著修改 tests/Feature/ExampleTest.php,寫看看首頁的測試:

class ExampleTest extends TestCase
{
/**
* @test
*/
public function indexPage()
{
$response = $this->get('/');

$response->assertStatus(200);
$response->assertSee('產品分類');
$response->assertSee('管理員頁面');
$response->assertSee('聯絡我們');
$response->assertSee('查看購物車');
$response->assertSee('回首頁');
}
}

接著跑測試:

$ php vendor/bin/phpunit

這裡大家可以試著做一件事:把 Server 關掉,再跑一次測試看看。

大家會發現,其實測試還是能正常執行的。這是因為 Laravel 把輸出頁面的方法,包成了物件,直到 route 處理到某個階段的時候,才輸出網頁;Laravel 的測試則是在輸出到網頁前,把物件擋下來然後拿來當字串(HTML)驗證。

這個做法好處當然很多,最大的好處是:只要能跑 PHP,不需要起伺服器就能執行測試。這執行測試的機會變得更大(因為需要的條件變少了);同樣的,不起伺服器,就少了非常多傳輸成本,速度肯定是更快的。

有一好沒兩好,它的壞處就是無法完全模擬真實的環境,包括 HTTP Server 或是瀏覽器執行 Javascript 等。除此之外,因為會使用一個 process 執行所有測試,這代表所有測試的記憶體是共享的,那就得小心單例模式全域影響的問題或是自動載入的問題等。

首頁測好了,那我們來測看看管理頁:

/**
* @test
*/
public function shouldBeOkWhenSeeAdminPage()
{
$response = $this->get('/admin.php');

$response->assertStatus(200);
$response->assertSee('管理功能');
$response->assertSee('統計功能');
}

但它會發生錯誤:

1) Tests\Feature\ExampleTest::shouldBeOkWhenSeeAdminPage
Expected status code 200 but received 404.

它說「預期會拿到 status code 200 但事實上拿到了 404 」,這很有可能是因為我們 Route 並沒有設定好,來調整一下 routes/web.php

// 忘了在後面加 .php
Route::get('/admin.php', function () {
ob_start();
require_once __DIR__ . '/../admin.php';
return ob_get_clean();
});

這裡調整完後,會發生很杯具的錯誤:

1) Tests\Feature\ExampleTest::shouldBeOkWhenSeeAdminPage
Expected status code 200 but received 500.

它說「預期會拿到 status code 200 但事實上拿到了 404 」,查了一下 log,它說找不到 tpl 這個變數:

Undefined variable: tpl at /Users/miles.chou/GitHub/MilesChou/book-refactoring-30-days/admin.php:194

往源頭找 $tpl 是在 config.php 設定的,找不到代表沒有載入過。這正是前面有提到的「自動載入」問題,因為 config.php 曾被 require 過,所以這裡就不會再 require 了,才會找不到變數。最簡單的解法是:把 require_once 改成 require,不過十之八九都有可能出事,就先試看看。

改完跑完之後,出現別的錯誤了:

DEBUG_MODE already defined at /Users/miles.chou/GitHub/MilesChou/book-refactoring-30-days/config.php:14

這昨天有提到解法如下:

defined('DEBUG_MODE') or define('DEBUG_MODE', env('APP_DEBUG'));

後面也有許多類似的錯誤都一起解掉,再跑一次應該就會過了。


今天成功把基本的驗收測試加上去了,明天我們再換跑瀏覽器的測試。

程式碼可以參考 GitHub PR