iT邦幫忙

2026 iThome 鐵人賽

DAY 29
0
Software Development

同一套 Laravel 系統,測試怎麼寫才不會說謊系列 第 29 篇

Day 29:首頁測試——一個頁面測試要先準備多少「全站狀態」

  • 分享至 

  • xImage
  •  

前言

「明明只是測首頁能不能正常打開,為什麼 beforeEach 要設定這麼多東西?」

首頁通常是一個系統裡最「熱鬧」的頁面——同時組合了好幾個不同來源的內容區塊,也最容易依賴一堆橫跨全站的設定值。今天要拆這個系統的首頁測試,看它的 beforeEach 準備了什麼、每個測試各自驗證哪個區塊,藉此討論「頁面測試的相依範圍該怎麼控制在合理大小」這個問題。

今日目標

  • 看首頁測試的 beforeEach 準備了哪些全站層級的設定
  • 理解「組合頁面」的測試策略:每個測試聚焦一個區塊,不要一次驗證全部
  • 看到一個排序邏輯的多層測試:優先序規則怎麼被拆成好幾支測試各自驗證
  • 學會判斷「這支測試的相依範圍會不會太大」,避免每次都要重建半個網站的狀態

首頁測試的起手式:先準備全站設定

beforeEach(function () {
    $settings = app(GeneralSettings::class);
    $settings->site_name = '示範大學';
    $settings->related_links = [];
    $settings->sso_login = true;
    $settings->save();
});

這個系統用 spatie/laravel-settings 套件管理橫跨全站的設定值(跟 Filament 後台可以編輯的設定頁綁在一起)。首頁的 layout 會用到站台名稱之類的全站設定,所以每一支首頁測試開跑前,都要先準備好這幾個設定值,不然頁面在渲染的過程中可能會因為缺少必要設定而出錯。光是這三行 beforeEach,就已經在告訴你:這是一個「相依範圍」不小的測試——首頁不是一個獨立的功能,它天生就跟好幾項全站層級的狀態綁在一起。

每個測試聚焦一個區塊

準備好全站狀態之後,實際的測試案例反而寫得很聚焦,每一支只驗證首頁上的一個區塊:

test('displays headline news from category 7', function () {
    $news = News::factory()->count(3)->create([
        'news_category_id' => 7,
        'published' => true,
        'published_at' => now()->subDay(),
    ]);

    $response = get('/');

    $response->assertOk();
    foreach ($news as $item) {
        $response->assertSee($item->title);
    }
});

test('headline displays published webcasts', function () {
    Webcast::factory()->count(3)->create(['published' => true]);
    Webcast::factory()->count(2)->create(['published' => false]);

    $response = get('/');

    $response->assertOk();
});

第一支只關心「頭條新聞區塊有沒有正確顯示指定分類的新聞」,第二支只關心「已發布的影音頻道會出現、未發布的不會」——**每支測試只針對首頁上的一個區塊建立測試資料、只驗證那個區塊的行為,不會在同一支測試裡把所有區塊的資料都準備齊全再一次驗證完。**這樣拆開的好處是,當某支測試失敗,你能立刻知道是首頁的哪個區塊出了問題,不用在一支塞滿各種資料的巨型測試裡大海撈針。

排序規則,拆成好幾支測試各自驗證

首頁的頭條區塊有一套排序優先序:新聞排在影音之前、置頂內容排在一般內容之前、同一層級內按發布時間新到舊排序。這個系統把這套規則拆成三支獨立的測試各自驗證:

test('headline sorts news before webcasts', function () {
    $webcast = Webcast::factory()->create(['title' => '活動快訊', 'published' => true]);
    $news = News::factory()->create([
        'title' => '頭條新聞',
        'news_category_id' => NewsCategory::HEADLINE_ID,
        'published_at' => now()->subDay(),
    ]);

    $response = get('/');

    $response->assertSeeInOrder(['頭條新聞', '活動快訊']);
});

test('headline sorts on_top news first', function () {
    $normal = News::factory()->create([
        'title' => '一般新聞', 'on_top' => false, 'published_at' => now(),
    ]);
    $pinned = News::factory()->create([
        'title' => '置頂新聞', 'on_top' => true, 'published_at' => now()->subWeek(),
    ]);

    $response = get('/');

    $response->assertSeeInOrder(['置頂新聞', '一般新聞']);
});

test('headline sorts by published_at desc within same on_top', function () {
    // 同層級內,發布時間新到舊排序
});

用 assertSeeInOrder() 直接驗證畫面上文字出現的先後順序,而不是去解析回應內容、比對陣列——這是一個很實用的斷言技巧:排序規則的驗證,不需要拆到「資料庫查詢回傳的陣列順序對不對」這麼底層,只要確認畫面上呈現的順序符合預期就夠了,因為使用者真正在意的就是畫面上看到的順序。

三支測試各自只操弄「排序規則裡的其中一個維度」(類型優先、置頂優先、時間排序),刻意不把三個維度混在同一支測試裡一次驗證——如果混在一起,一旦測試失敗,你很難判斷是哪一條排序規則出了問題。

這篇給我們的提醒

首頁測試示範了兩個層次的「相依範圍控制」:全站層級(beforeEach 準備必要的全站設定,讓每支測試不用重複這段樣板)跟單一測試層級(每支測試只準備並驗證跟自己相關的那一小塊資料,不貪心地想一次驗證全部)。組合式頁面測試最容易犯的錯誤,是每支測試都想著「反正首頁很多東西,乾脆都準備齊全一起測」——這樣寫出來的測試會又慢又難讀,失敗時也不容易定位問題。把相依範圍控制在「這支測試真正需要的最小狀態」,才是讓組合頁面測試維持可讀性的關鍵。

今日思考題

回想你維護的專案裡,有沒有一支「組合式頁面」的測試,beforeEach 或測試本體裡準備了一堆跟這支測試實際要驗證的行為無關的資料?如果拆掉這些不相關的準備工作,這支測試會不會變得更好懂?

今日重點回顧

  • 首頁測試的 beforeEach 準備了全站層級的設定值,反映出組合頁面天生的相依範圍
  • 每支測試只聚焦首頁上的一個區塊,不在同一支測試裡塞滿所有區塊的驗證
  • 排序優先序規則被拆成三支測試,各自只操弄一個排序維度,方便定位問題
  • 用 assertSeeInOrder() 直接驗證畫面呈現順序,不需要拆到底層資料結構
  • 控制測試的相依範圍在「真正需要的最小狀態」,是組合頁面測試維持可讀性的關鍵

明日預告

明天是這個系列的最後一天,要把前面 29 天拆過的所有測試層次收攏起來,回頭看這 63 支測試教會我的分層策略——包含幾個誠實的取捨跟沒有做到位的地方。


上一篇
Day 28:一個曾經出過 Content-Type bug 的路由,測試覆蓋盲點在哪裡
下一篇
Day 30:總結——這 63 支測試教我的分層策略
系列文
同一套 Laravel 系統,測試怎麼寫才不會說謊 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言