雲端 Mac 驗收 iOS PDF 匯出:以 PDFKit 檢查頁面與內容

雲端 Mac 驗收 iOS PDF 匯出:以 PDFKit 檢查頁面與內容

在 iOS App 中點選報表的「匯出」後,分享面板也顯示了檔案,但接收方開啟時卻發現少了一頁。遇到這類問題,只確認匯出函式回傳成功並不足夠:測試工作必須取得最終的 PDF 檔案,檢查能以程式判定的內容,再將其餘部分交由人工目視複核。以下流程適合在雲端 Mac 上執行模擬器測試,也能在本機照做。

先定義可驗收的檔案

選定一份固定輸入,例如兩筆明細與明確的報表標題。測試案例應事先知道預期檔名、最少頁數,以及一段固定的內文。不要直接對產生時間、隨機編號或金額總計做字串斷言;這些欄位可能隨測試資料改變。較穩妥的做法是固定輸入資料,並為動態欄位另外訂出明確的格式規則。

讓測試版 App 將 PDF 寫入自身的 Documents/export.pdf。這是為了方便驗收,不代表正式產品必須使用相同的交付路徑。每次執行前先刪除舊檔,匯出完成後再確認檔案存在且非空,否則上一次的產物可能讓這次測試誤判為通過。

檔案存在、能夠解析、內容正確,是三項不同的檢查。通過前一項,不能取代後一項。

從模擬器取得本次產物

先使用測試工作指定的模擬器識別碼,執行 App 與匯出操作。工作結束後,在同一識別碼對應的裝置上查詢 App 容器;不要靠 booted 猜測目標,尤其同時啟動多台模擬器時,很容易取錯。下例的識別碼與 App 套件識別碼應由測試設定提供:

UDID="替换为测试使用的模拟器标识"
BUNDLE_ID="替换为测试应用的包名"
CONTAINER="$(xcrun simctl get_app_container "$UDID" "$BUNDLE_ID" data)" || exit 1
mkdir -p artifacts
test -s "$CONTAINER/Documents/export.pdf" || exit 1
cp "$CONTAINER/Documents/export.pdf" artifacts/export.pdf
file artifacts/export.pdf

get_app_container 要求 App 已安裝在指定的模擬器上。查詢失敗時,先核對測試實際使用的裝置識別碼、套件識別碼及安裝結果,而不是改為搜尋整個磁碟。複製後要檢查的是工作目錄中的檔案;後續解析與封存也都使用這份副本,避免 App 再次寫入時改變檢查對象。file 的輸出有助於診斷,但不能取代 PDF 解析。

用 PDFKit 檢查結構與文字

將以下 Swift 程式碼儲存為 scripts/check-pdf.swift。它接收檔案路徑與預期詞句,逐頁檢查頁面邊界,並確認至少有一頁包含該詞句。若結束碼不是零,建置工作即可判定這次自動檢查未通過。

import Foundation
import PDFKit

guard CommandLine.arguments.count == 3,
      let pdf = PDFDocument(url: URL(fileURLWithPath: CommandLine.arguments[1])),
      pdf.pageCount > 0 else {
    fputs("PDF is missing or cannot be parsed\n", stderr)
    exit(2)
}

let expected = CommandLine.arguments[2]
var extracted = ""
for index in 0..<pdf.pageCount {
    guard let page = pdf.page(at: index) else {
        fputs("PDF page is missing\n", stderr)
        exit(2)
    }
    let media = page.bounds(for: .mediaBox)
    let crop = page.bounds(for: .cropBox)
    guard media.width > 0, media.height > 0,
          crop.width > 0, crop.height > 0,
          media.contains(crop) else {
        fputs("PDF page bounds are invalid\n", stderr)
        exit(2)
    }
    extracted += page.string ?? ""
}
guard extracted.contains(expected) else {
    fputs("Expected text was not extracted\n", stderr)
    exit(2)
}
print("Checked \(pdf.pageCount) PDF page(s)")

執行 swift scripts/check-pdf.swift artifacts/export.pdf "Quarterly Report"。請將範例詞句換成固定測試資料中確實存在的文字。如果需求規定恰好兩頁,還應將 pdf.pageCount == 2 加入斷言;只檢查頁數大於零,無法發現分頁變化。頁面尺寸也可以依產品需求分別核對,但不要在未有約定的情況下,將所有合法的紙張規格都判為錯誤。

文字擷取不等於視覺驗收

page.string 檢查的是可擷取的文字,不保證字形未被裁切,也無法證明圖表、圖片或色彩正確。反過來說,掃描檔或繪製成圖片的文字,即使看起來正常,也可能無法擷取。文字斷言失敗時,先保留原始 PDF,判斷是內容沒有寫入,還是該頁不適用這種擷取方式;不要只為了讓工作通過就直接刪除斷言。

將失敗定位到具體環節

建議每次執行時分別記錄匯出操作結果、檔案複製結果與 PDFKit 結束碼。發生失敗時,就能依序判斷:App 是否執行匯出、容器中是否產生了本次檔案、副本能否解析,以及頁數、邊界和文字是否符合預期。不要只輸出一句「PDF 測試失敗」,否則排查時仍得重新執行整套流程。

現象 優先核對
找不到容器 模擬器識別碼、套件識別碼、安裝結果
檔案為空 是否清除舊檔、寫入是否完成
無法解析 複製過程、最終檔案格式
文字不符 固定輸入、頁面內容、文字擷取的限制

失敗時封存 artifacts/export.pdf 與測試日誌,但要先確認報表不含真實使用者資料。如果檔案可能包含敏感資訊,請改用合成樣本、限制產物存取,並依團隊約定清理。不要把正式環境的報表上傳至公開的 CI 流程產物。

以一次可複查的執行收尾

提交驗收流程前,用同一份固定輸入連續執行兩次:兩次都應重新匯出、覆寫目標檔案,並得到一致的頁數與文字檢查結果。接著刻意移除預期詞句,確認腳本會失敗;再讓測試版 App 不產生檔案,確認複製步驟會失敗。這兩項反向檢查能排除「斷言根本沒有執行」的情況。

自動檢查通過後,仍須開啟具代表性的 PDF 樣本,檢視分頁交界、長文字換行及圖表邊緣。機器負責驗證穩定的結構與文字要求,人工則確認實際呈現效果;將兩者的結論分開記錄,下次匯出發生異常時才有清楚的比較基準。

常見問題

PDFKit 擷取不到文字,是否代表匯出失敗?

不一定。掃描影像、轉成圖片的文字或特殊字型可能無法擷取;應保留檔案並檢查實際畫面。

如何將模擬器中的 PDF 交給驗收工作?

先讓應用程式將檔案寫入 Documents,再以 simctl get_app_container 找到資料目錄,複製到工作成果目錄。

把下一次建置交給專屬雲端 Mac。

確認 ViewMini 的機型、地區與租期,再設定符合工作流程的獨享實體節點。

設定雲端 Mac

再確認一次設備與租期。

三款在售機型提供日租、週租、月租與季租價格;下單前請查看完整方案。

查看機型與價格 設定雲端 Mac