GitHub Actions 自動化測試流程
在現代軟體開發中,持續整合(Continuous Integration, CI)已經不是一個「選項」,而是一個「必需品」。它能確保我們每次提交的程式碼都經過自動化測試,及早發現問題,從而提升整個團隊的開發品質與效率。對於專案來說,一個好的 CI 流程更是不可或缺。
我們將深入解析一個為 Django 專案設計的 GitHub Actions 工作流程。這個流程不僅涵蓋了基本的測試,還巧妙地結合了原生 Python 環境與 Docker Compose 環境,實現了快速回饋與高擬真度測試的雙重保障。
GitHub Actions 核心概念
在深入程式碼之前,先快速了解幾個 GitHub Actions 的核心概念:
- Workflow(工作流程): 由一個或多個
job
組成,定義了整個自動化過程。通常以 YAML 檔案形式存放在專案的.github/workflows/
目錄下。 - Event(事件): 觸發工作流程的特定活動,例如
push
(推送到儲存庫)或pull_request
(發起拉取請求)。 - Job(任務): 工作流程中的一個執行單元,會在一台虛擬機(
runner
)上執行。一個工作流程可以有多個任務,它們可以並行或依序執行。 - Step(步驟): 任務中的一個獨立指令或動作。一個任務由多個步驟組成。
- Action(動作): 可重用的程式碼單元,是建構步驟的積木。例如
actions/checkout
就是一個官方提供的 Action,用來拉取你的程式碼。
工作流程檔案解析 (Django CI Tests
)
讓我們來看看這次的主角,這個精心設計的 Django CI Tests
工作流程。
name: Django CI Tests
on: [push, pull_request]
jobs:
# ... 兩個 jobs 的定義將在下面詳細解說 ...
name: Django CI Tests
:為這個工作流程取一個易於辨識的名字。on: [push, pull_request]
:這行是關鍵!它定義了觸發條件。這意味著,每當有新的程式碼被推送到任何分支,或者當有人發起一個 Pull Request 時,這個自動化測試流程就會被啟動。
這個工作流程包含了兩個並行執行的任務(Jobs):native-pytest
和 docker-compose-pytest
。讓我們逐一拆解它們的設計理念與實現細節。
任務一:native-pytest
- 追求極速的原生 Python 測試
第一個任務的目標是「快」。它在一個乾淨的 Ubuntu 環境中直接設定 Python,並執行測試。這適用於那些不依賴外部服務(如資料庫、快取)的單元測試,能夠在最短時間內給予開發者回饋。
native-pytest:
name: Native Python Pytest
runs-on: ubuntu-latest
defaults:
run:
working-directory: backend
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
DEBUG: "True"
USE_POSTGRES: "False" # 關鍵點:不使用 PostgreSQL
ALLOWED_HOSTS: localhost,127.0.0.1
SOCIAL_AUTH_GOOGLE_CLIENT_ID: ${{ secrets.SOCIAL_AUTH_GOOGLE_CLIENT_ID }}
SOCIAL_AUTH_GOOGLE_CLIENT_SECRET: ${{ secrets.SOCIAL_AUTH_GOOGLE_CLIENT_SECRET }}
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install uv
run: pip install uv
- name: Install backend dependencies
run: uv sync
- name: Run migrations
run: uv run python manage.py migrate
- name: Run pytest
run: uv run pytest -v --tb=short --maxfail=5 --disable-warnings
拆解步驟:
-
基本設定:
runs-on: ubuntu-latest
: 指定任務運行在最新版的 Ubuntu 虛擬機上。defaults.run.working-directory: backend
: 這是一個很方便的設定,它將後續所有run
指令的預設工作目錄都設定為backend
,省去了在每個步驟中都cd backend
的麻煩。
-
環境變數 (
env
):- 這裡設定了 Django 運作所需的環境變數。
SECRET_KEY
等敏感資訊透過${{ secrets.SECRET_KEY }}
從 GitHub 的 Secrets 中安全地讀取,避免了硬編碼在程式碼中。- 最重要的設定是
USE_POSTGRES: "False"
。這通常會告訴 Django 的settings.py
使用輕量級的 SQLite 作為測試資料庫,因為它不需要額外的服務,啟動速度極快。
-
執行步驟 (
steps
):actions/checkout@v4
: 第一步總是它!這個 Action 會將你的專案程式碼拉取到虛擬機中。setup-python@v5
: 設定指定的 Python 版本(這裡用了最新的 3.13!)。Install uv
&uv sync
: 這裡使用uv
這個新一代的超高速 Python 套件管理器來取代傳統的pip
。uv sync
會根據pyproject.toml
或requirements.txt
檔案來安裝所有依賴,速度飛快。uv run python manage.py migrate
: 在執行測試前,先執行資料庫遷移。對於 SQLite 來說,這會在記憶體或一個暫存檔案中建立資料庫結構。uv run pytest ...
: 核心步驟!uv run
會在uv
管理的虛擬環境中執行pytest
。-v
: 顯示詳細的測試結果。--tb=short
: 使用簡潔的錯誤追蹤格式。--maxfail=5
: 當有 5 個測試失敗時就立即停止,節省時間。--disable-warnings
: 隱藏不影響功能的警告,讓輸出更乾淨。
小結:native-pytest
的優勢是速度快、設定簡單,適合快速驗證程式碼邏輯。
任務二:docker-compose-pytest
- 模擬生產環境的高擬真度測試
第二個任務的目標是「擬真」。它使用 Docker Compose 來啟動整個應用程式環境,包括 Django 應用本身和 PostgreSQL 資料庫。這確保了測試環境與你的開發環境,甚至生產環境,都盡可能地保持一致。
docker-compose-pytest:
name: Docker Compose Pytest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Create backend .env file
run: |
echo "SECRET_KEY=${{ secrets.SECRET_KEY }}" > backend/.env
# ... 其他 echo 指令 ...
echo "USE_POSTGRES=True" >> backend/.env
echo "POSTGRES_HOST=db" >> backend/.env
# ...
- name: Build docker-compose services
run: docker compose -f docker-compose.dev.yml build
- name: Run pytest in docker-compose
run: docker compose -f docker-compose.dev.yml run --rm backend uv run pytest -v --tb=short --maxfail=5 --disable-warnings
- name: Check container logs on failure
if: failure()
run: docker compose -f docker-compose.dev.yml logs backend
- name: Shutdown docker-compose
run: docker compose -f docker-compose.dev.yml down -v
拆解步驟:
-
建立
.env
檔案:- 這是此任務中最巧妙的一步。Docker Compose 通常會讀取一個
.env
檔案來設定容器的環境變數。 - 這個步驟動態地建立了一個
backend/.env
檔案,並將 GitHub Secrets 和其他設定值寫入其中。 - 注意這裡的關鍵差異:
USE_POSTGRES
被設為"True"
,並且新增了所有POSTGRES_*
相關的變數,例如資料庫主機POSTGRES_HOST=db
(db
是docker-compose.dev.yml
中定義的資料庫服務名稱)。
- 這是此任務中最巧妙的一步。Docker Compose 通常會讀取一個
-
建置與執行 Docker Compose:
docker compose ... build
: 根據docker-compose.dev.yml
和相關的Dockerfile
來建置服務的映像檔。docker compose ... run --rm backend ...
: 這是執行測試的核心指令。run backend
: 告訴 Docker Compose 啟動backend
服務(以及它所依賴的db
服務),並在backend
容器內執行後續的指令。--rm
: 表示指令執行完畢後,自動刪除這個臨時建立的容器,保持環境乾淨。- 後面的
uv run pytest ...
指令與任務一相同,但這次,它是在一個包含完整資料庫連線的 Docker 容器內執行的。
-
偵錯與清理:
if: failure()
: 這是 GitHub Actions 的一個強大功能。這個步驟只會在前面的步驟失敗時才會執行。docker compose ... logs backend
: 如果測試失敗,這個指令會印出backend
容器的日誌,這對於排查連線問題或環境錯誤非常有幫助。docker compose ... down -v
: 無論成功或失敗,最後都要執行清理工作。down
會關閉並移除所有容器和網路,-v
則會一併刪除相關的 volumes(例如資料庫數據),確保每次執行都是從零開始的乾淨狀態。
小結:docker-compose-pytest
的優勢是環境擬真度高,能測試應用與資料庫等外部服務的整合,確保程式碼在類生產環境下也能正常運作。
為什麼需要兩種測試策略?
這個工作流程最精彩的設計,就是同時採用了這兩種策略:
- 快速回饋:開發者推送一個小修改後,
native-pytest
會在 1-2 分鐘內給出結果。如果只是修改了一個不涉及資料庫的函式,可以很快知道有沒有改壞。 - 深度驗證:
docker-compose-pytest
雖然慢一些(可能需要 3-5 分鐘),但它提供了更全面的保障。它能捕捉到原生測試無法發現的問題,例如錯誤的資料庫查詢、環境變數設定錯誤等。
當你在 GitHub 上看到一個 Pull Request 時,這兩個任務會並行執行。你將會看到兩個綠色的勾勾,這代表你的程式碼不僅通過了快速的單元測試,也通過了嚴苛的整合測試,讓你有十足的信心去合併它。
總結
透過這個 Django CI Tests
工作流程,學習到如何利用 GitHub Actions 建立一個高效且可靠的自動化測試流程。它結合了:
- 事件驅動的自動化 (
on: [push, pull_request]
)。 - 使用 GitHub Secrets 安全管理敏感資訊。
- 利用原生環境實現快速測試。
- 利用 Docker Compose 模擬生產環境進行整合測試。
- 巧妙的條件執行 (
if: failure()
) 來輔助偵錯。
將這樣的 CI 流程整合到專案中,無疑會大大提升開發品質與部署信心!