「昨天不是已經測過遷移指令了嗎?今天還要再測一次檔案路徑?」
昨天測的是「遷移指令這整個流程」——它能不能正確找到舊格式的檔案、能不能正確搬到新路徑。但如果搬移邏輯本身沒問題,新上傳的檔案卻從一開始就沒有照著自訂規則產生正確路徑呢?昨天的測試不會告訴你答案,因為它假設的前提是「上傳流程本身是對的」。今天要拆的是更小的一個單位:自訂的路徑產生規則本身,到底對不對。
PathGenerator 怎麼被獨立測試,不牽扯任何遷移邏輯這個系統自訂了一個 ModelPathGenerator,取代 Media Library 套件預設的路徑規則,讓上傳的檔案依照 Model 類型跟 ID 分資料夾(例如某個 Model 底下的圖片存在 對應資料夾/{model_id}/{media_id}/檔名 這樣的結構),方便直接從路徑推算出這個檔案屬於哪一筆資料,不用另外查資料庫。
昨天的遷移指令測試,關心的是「舊資料能不能被正確找到並搬移」,測試的起點就假設了「新上傳的檔案,套件會照著自訂規則產生正確路徑」——但這個假設本身有沒有錯,昨天的測試回答不了,因為它的重點是搬移邏輯,不是路徑產生邏輯。
如果你把「大流程對不對」跟「流程裡每個小單位對不對」混在同一組測試裡驗證,一旦測試失敗,你很難第一時間知道問題出在哪一層。
beforeEach(function () {
Storage::fake('public');
config(['media-library.path_generator' => ModelPathGenerator::class]);
});
test('Banner upload stores file under 對應目錄/{model_id}/{media_id}/', function () {
$banner = Banner::factory()->create();
$banner->addMedia(UploadedFile::fake()->image('hero.jpg'))
->toMediaCollection('default');
$media = $banner->getFirstMedia();
$expectedPath = "banner/{$banner->id}/{$media->id}/hero.jpg";
Storage::disk('public')->assertExists($expectedPath);
});
這支測試只做一件事:真的對一個 Banner Model 呼叫 addMedia()->toMediaCollection(),然後檢查磁碟上有沒有出現預期路徑的檔案。它完全不管遷移指令、不管舊資料,單純驗證「這個 Model、上傳這個檔案,最後產生的路徑對不對」。
同一份測試檔案裡,同樣的驗證邏輯,會重複套用在另外兩種不同的 Model 上:
test('News upload stores file under 對應目錄/{model_id}/{media_id}/', function () {
$news = News::factory()->create();
$news->addMedia(UploadedFile::fake()->image('photo.png'))->toMediaCollection('images');
$media = $news->getFirstMedia('images');
Storage::disk('public')->assertExists("news/{$news->id}/{$media->id}/photo.png");
});
test('Webcast upload stores file under 對應目錄/{model_id}/{media_id}/', function () {
$webcast = Webcast::factory()->create();
$webcast->addMedia(UploadedFile::fake()->image('thumb.jpg'))->toMediaCollection('default');
$media = $webcast->getFirstMedia();
Storage::disk('public')->assertExists("webcast/{$webcast->id}/{$media->id}/thumb.jpg");
});
如果只測一種 Model(例如只測 Banner),這條規則可能只是「剛好在這個 Model 上成立」,沒辦法確定它是不是真的「對所有 Model 都適用的通用規則」。分別對 Banner、News、Webcast 三種完全不同的 Model 各測一次,才能確認這個 PathGenerator 的邏輯是根據 Model 類型動態產生的,不是寫死了某一種特例。
同一支測試檔案裡,還額外驗證了兩件跟「路徑格式」有關、但角度不同的事:
getUrl() 回傳的網址是不是真的包含了正確的路徑片段(不只是磁碟上存在這個檔案,套件回傳給前端使用的網址也要對)把這篇跟昨天的遷移指令測試放在一起看,會看到一個清楚的分工:
**先確定小單位本身正確,再測包住它的整個流程,這樣當某個測試失敗時,你能立刻知道問題出在「規則本身錯了」還是「規則套用的流程錯了」,不用每次都從頭排查整條路徑。**如果只寫了昨天那支遷移指令測試,沒有今天這支,一旦路徑規則本身壞掉,你只會看到遷移指令測試失敗,卻不容易第一眼看出問題根源在路徑規則,還是在搬移邏輯本身。
回想你維護的專案,有沒有一個小單位的邏輯(例如一個格式轉換函式、一個路徑產生規則)從來沒有被獨立測試過,只靠包住它的大流程測試間接覆蓋?如果那個小單位本身壞了,現有的測試多久之後才會發現?
PathGenerator 被獨立拉出來測,完全不牽扯任何遷移邏輯,只驗證「這個 Model、上傳這個檔案,該產生什麼路徑」明天要換一個完全不同的主題:時間相依的邏輯怎麼測。一支會依賴「現在幾點」來決定行為的 Job,如果不特別處理,測試可能在某些日期跑會過、某些日期跑會壞。