0.はじめに
前回GitHub Actions と Cloud Run で実現する CI/CD ハンズオンで、GitHubにマージすることで、自動的にCloudRunにデプロイされる仕組みを構築しました。
今回は、そちらに新しく テスト を組み込んで、マージしたらテストが実行され、問題ない場合は自動的にCloudRunにデプロイされる仕組みを構築したいと思います。
1.基本的な用語
1.1.テストについて
ソフトウェア開発における「テスト」とは、作成したコードが意図した通りに動作するかを確認する作業のことです。
自動化されたテストは、手作業に比べて高速かつ繰り返し正確に実行できるため、開発効率を高め、品質を均一に保つ上で非常に有効です。
そしてCI/CDパイプラインにおいては、この自動テストが重要な役割を果たします。
1.2.テストの種類
今回はテストの中でも 単体テスト(Unit Test)部分について実施をしていきます。
項番 | テスト内容 | テスト詳細 |
---|---|---|
1 | 単体テスト(Unit Test) | プログラムの最小単位(関数やメソッドなど)が正しく動作するかを確認するテスト |
2 | 統合テスト(Integration Test) | 複数のモジュールやコンポーネントを組み合わせたときに、正しく連携して動作するかを確認するテスト |
3 | システムテスト | システム全体が仕様通りに機能するかを確認するテスト。すべてのコンポーネントを組み合わせた状態でテストする |
4 | 運用テスト(Operation Test) | 実際の運用環境に近い状態で、システム全体の動作やパフォーマンスを確認するテスト |
1.3.利用ツール(pytest)について
Pythonで広く使われているテストフレームワークで、シンプルにテストを書くことが可能です。
特別な定型コードが少なく、Pythonの標準的な機能を使ってテスト関数を定義できます
2.ハンズオン
2.0.前提条件
- Google Cloud プロジェクトが作成済みで、課金が有効になっていること
- GitHubにアカウントが作成済みであること
- GitHub CLI がインストール済みであること
- 本手順は前回ブログGitHub Actions と Cloud Run で実現する CI/CD ハンズオン に引き続きで構築をしていきます
2.1.ローカルでテスト実施
テスト対象のコードmain.py
Web画面を標示した際「Hello World!!!」が表示されることを正しいとする
# main.pyの内容
import functions_framework
@functions_framework.http
def hello_http(request):
return "Hello World!!!"
# requirements.txtの内容
functions-framework==3.*
2.1.1.テストファイル作成
# Pythonアプリのテストファイルを作成
# test_main.py
cat > test_main.py << EOF
# テスト対象のファイルをインポート
import main
def test_hello_http():
"""
hello_http関数が'Hello World!!!'を返すことを確認するテスト。
このテストはAPIのグリーティングメッセージが正しいことを検証します。
"""
# 引数をNone(無し)で hello_http関数を実際に呼び出し
response = main.hello_http(None)
# 関数の戻り値が"Hello World!!!"と等しい
assert response == "Hello World!!!"
EOF
2.1.2.ライブラリインストール
# requirements-testファイルを作成
cat > requirements-test.txt << 'EOF'
pytest==7.*
EOF
# テスト用ライブラリインストール
pip install -r requirements-test.txt
2.1.3.テスト実施
- テスト(pytest)実施後、「1 passed」というレスポンスがあり、テストが成功していることを確認
# テスト実行
pytest
# 【レスポンス(一部省略)】
== test session starts ==
platform linux -- Python 3.12.3, pytest-7.4.4, pluggy-1.5.0
rootdir: /home/tetutetu/my-cloudrun-app
plugins: cov-4.1.0
collected 1 item
test_main.py . [100%]
== 1 passed in 0.30s ==
2.2.GitHub Actionsの準備
2.2.1.Workflows修正
- GitHub Actions のワークフローファイルを更新し、自動テストをパイプラインに組み込む
# GitHub Actionsワークフローファイルを作成
cat > .github/workflows/cloudrun-deploy.yml << 'EOF'
name: Build and Deploy to Cloud Run
# masterブランチにプッシュされたタイミング
on:
push:
branches: [ master ]
# 環境変数
env:
SERVICE_NAME: test-service
REGION: asia-northeast1
# 実行する作業の定義
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
# コードを取得するステップ
- name: Checkout code
uses: actions/checkout@v4
### ここから 新規追加ステップ部分 ###
# GitHubでの仮想マシン(ランナー)のPythonセットアップ
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.12'
# 依存関係のインストール
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-test.txt
# テストを実行するステップ
- name: Run tests
run: |
pytest
### ここまで 新規追加ステップ部分 ###
# Google 認証のステップ
- name: Authenticate to Google Cloud
if: success() # テストが成功した場合のみ実行
uses: google-github-actions/auth@v2
with:
credentials_json: ${{ secrets.GCP_SA_KEY }}
project_id: ${{ secrets.GCP_PROJECT_ID }}
# SDKによるコマンドを利用できるようにするステップ
- name: Set up Cloud SDK
if: success() # テストが成功した場合のみ実行
uses: google-github-actions/setup-gcloud@v2
with:
project_id: ${{ secrets.GCP_PROJECT_ID }}
# デバッグ認証ステップ
- name: Debug Authentication
if: success() # テストが成功した場合のみ実行
run: |
echo "Current project: $(gcloud config get-value project)"
echo "Current account: $(gcloud auth list --filter=status:ACTIVE --format='value(account)')"
# Cloud Runにデプロイ (テストが成功した場合のみ)
- name: Deploy to Cloud Run
if: success() # テストが成功した場合のみ実行
run: |
gcloud config set project ${{ secrets.GCP_PROJECT_ID }}
gcloud run deploy ${{ env.SERVICE_NAME }} \
--source . \
--project ${{ secrets.GCP_PROJECT_ID }} \
--platform managed \
--region ${{ env.REGION }} \
--allow-unauthenticated
EOF
2.2.2.ファイルをコミットしてプッシュ
# ファイルをコミットしてプッシュ
git add .
git commit -m "Update Workflowsyaml"
git push
2.3.push後の確認
2.3.1.GitHubでの画面
- テストが追加されたことで、GitHubのデプロイフローにも項目が追加されることが確認できる
項番 | 手順名 | 内容 |
---|---|---|
1 | Set up Python | テスト(Python)環境をセットアップするステップ |
2 | Install dependencies | テストに必要なライブラリをインストールするステップ |
3 | Run tests | pytestコマンドを実行 |
4 | Post Set up Python | 環境のクリーンアップ |
- 「Run tests」項目の内容
ローカルで実行した内容と同じものが出力される
2.3.2.希望した内容と異なる(失敗)結果になった場合の画面(ローカル)
- ローカルでの画面
アプリのレスポンスをHello World
に修正(!マークを削除)して実行すると、想定された結果(Hello World!!!
)と異なるため1 failed
となります。
# テスト実行
pytest
# 【レスポンス(一部省略)】
== test session starts ==
platform linux -- Python 3.12.3, pytest-7.4.4, pluggy-1.5.0
rootdir: /home/lemoned_i_scream_art_of_noise/20250427_my-cloudrun-app
plugins: cov-4.1.0
collected 1 item
test_main.py F [100%]
== FAILURES ==
__ test_hello_http __
def test_hello_http():
"""
hello_http関数が'Hello World!!!'を返すことを確認するテスト。
このテストはAPIのグリーティングメッセージが正しいことを検証します。
"""
# 引数をNone(無し)で hello_http関数を実際に呼び出し
response = main.hello_http(None)
# 関数の戻り値が"Hello World!!!"と等しい
> assert response == "Hello World!!!"
E AssertionError: assert 'Hello World' == 'Hello World!!!'
E - Hello World!!!
E ? ---
E + Hello World
test_main.py:12: AssertionError
== short test summary info ==
FAILED test_main.py::test_hello_http - AssertionError: assert 'Hello World' == 'Hello World!!!'
== 1 failed in 0.22s ==
2.3.3.希望した内容と異なる(失敗)結果になった場合の画面(GitHub)
- GitHubでの画面
ローカルと同様に、GitHubの画面からもRun tests
で失敗していることが確認できます。失敗理由に関してもローカルで出力された内容と同様となっています。
3.おわりに
3.1.得られた知見
- テストとCI/CDの連携による開発プロセスの自動化
- テスト失敗時にデプロイが自動的に停止する仕組みの有効性
- 継続的インテグレーションによる早期のバグ発見
3.2.今後の課題
- より複雑なテストケースの追加(具体的な
pytest
の作成方法) - 複数の環境(開発・ステージング・本番)でのテスト戦略
- クラウドサービスのマネージドサービスとの連携強化