iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0
Software Development

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

Day 13:表單與表格測試分離——跟著架構重構一起發生的測試結構調整

  • 分享至 

  • xImage
  •  

前言

「重構程式碼結構的時候,測試也要跟著大改嗎?」

很多時候答案是「不用,測試驗證的是外顯行為,內部怎麼組織不影響測試怎麼寫」。但今天要講一個例外:這個系統有一次把表單跟表格的定義從 Resource 類別裡抽出來,變成獨立的 Schema/Table 類別,這次重構真的讓測試檔案跟著拆成兩支——而且拆開之後的測試手法,也跟以前不一樣了。

今日目標

  • 認識一次「把 Filament Resource 的 form/table 定義抽成獨立類別」的真實重構
  • 理解為什麼這次重構會讓測試檔案也跟著拆分
  • 看懂「驗證表單結構」跟「驗證表單提交後的行為」是兩種不同層級的測試
  • 建立判斷力:什麼樣的重構會連帶影響測試該怎麼寫

重構前後:表單定義去哪了

在這次重構之前,一個 Resource 類別裡同時塞著表單欄位定義、表格欄位定義、還有各種 action。重構之後,表單定義搬進獨立的 Schema 類別,表格定義搬進獨立的 Table 類別,Resource 本身只剩下組裝跟基本設定。這是一次典型的「單一職責」重構:原本一個類別要同時扛「畫面長什麼樣」跟「畫面怎麼互動」兩種責任,拆開之後每個類別只回答一個問題。

重構完成之後,對應的測試檔案也從一支拆成兩支:PageFormTest.php 只驗證表單的欄位結構,PageTableTest.php 只驗證表格的欄位結構跟排序邏輯。這不是巧合,是因為測試結構本來就該對應到程式碼的職責邊界——當程式碼的職責被拆乾淨,測試自然也會被拆乾淨,如果測試沒有跟著變乾淨,反而是一個「這次重構可能沒有真正拆對邊界」的警訊。

兩種不同層級的表單測試

驗證表單提交後的行為(Day 11 已經看過的手法)

livewire(PageResource\Pages\CreatePage::class)
    ->fillForm(['name' => '關於我們', 'slug' => 'about'])
    ->call('create')
    ->assertHasNoFormErrors();

expect(Page::where('slug', 'about')->exists())->toBeTrue();

這種寫法驗證的是「使用者填完表單、送出之後,系統有沒有正確反應」——測的是整個提交流程跑完之後的最終結果。

驗證表單結構本身有沒有配置對

livewire(PageResource\Pages\CreatePage::class)
    ->assertFormFieldExists('name')
    ->assertFormFieldExists('slug');

$form = $testable->get('form');
expect($form->getComponent('data.template'))
    ->toBeInstanceOf(Select::class);

這種寫法不送出表單,直接讀取 Livewire component 內部的 form 定義,斷言某個欄位存在、某個欄位是不是預期的元件類型(例如「模板」欄位是不是一個下拉選單而不是文字輸入框)。這驗證的是表單的結構配置本身,跟「填了資料之後會發生什麼事」是完全不同層級的問題——就算表單結構配置錯了(例如某個欄位型別選錯),只驗證提交行為的測試不一定抓得到,因為測試資料本來就是照著開發者以為的結構去填的。

為什麼要兩種測試都留著

只驗證提交行為,沒辦法保證表單本身的欄位型別、必填規則、選項來源配置對;只驗證表單結構,沒辦法保證使用者真的能順利完成一次操作。這兩種測試分別回答「這個表單長得對不對」跟「這個表單用起來對不對」兩個不同的問題,重構前混在同一支測試檔案裡的時候,容易只顧著測其中一種、漏了另一種;拆開之後,每一支測試檔案的職責範圍變清楚,反而更容易發現「這裡少測了什麼」。

今日思考題

回想你維護的專案裡,有沒有一次重構是把一個大類別拆成幾個各自負責單一職責的小類別?拆完之後,對應的測試檔案有沒有跟著變乾淨?如果測試檔案還是一大坨混在一起,會不會是這次重構的職責邊界其實沒有真正拆對?

今日重點回顧

  • 一次把表單/表格定義抽成獨立類別的重構,讓對應的測試檔案也從一支拆成兩支
  • 測試結構應該對應程式碼的職責邊界,測試沒跟著變乾淨可能代表重構沒拆對
  • 「驗證表單結構本身」跟「驗證表單提交後的行為」是兩種不同層級的測試,缺一不可
  • 好的架構重構通常會讓測試自然對應到單一職責,這是可以拿來驗證重構品質的訊號

明日預告

明天要講一支被跳過(skip)的登入測試——測試覆蓋率數字看起來很漂亮,但實際保護力可能遠比表面數字弱,這中間的落差是怎麼發生的。


上一篇
Day 12:這個專案沒有 Policy 的 unit test——為什麼「測行為」有時候比「測實作」更有效
下一篇
Day 14:一支被 skip() 掉的登入測試——測試覆蓋率數字 vs 實際保護力落差
系列文
同一套 Laravel 系統,測試怎麼寫才不會說謊14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言