0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flask + PostgreSQL の書籍管理アプリに Pytest と GitHub Actions (PostgreSQL サービスコンテナ) で CI 環境を構築した話

0
Posted at

はじめに

FlaskとPostgreSQLを用いたWebアプリ開発のハンズオンを完了したため、その応用実践として自分自身の書籍管理アプリ(book-log-app)の開発に取り組みました。

基本機能であるCRUDの実装から始まり、ハンズオンの流れを踏襲しながらpytestによる自動テスト、そしてGitHub ActionsでのCI環境(PostgreSQLサービスコンテナ連携)構築まで進めました。

本記事では、開発の流れと、特に何度も壁にぶち当たったCI/CD構築時のトラブルシューティングについて詳しく記録します。


開発の流れ(ハンズオンからのステップアップ)

  1. 環境構築 & DB設計
    • Python 3.12 / Flask / PostgreSQL 16 の構成。
    • updated_at 自動更新を含む books テーブルを作成し、.env で環境変数を分離。
  2. CRUD機能の実装
    • 新規登録(Create)、一覧表示(Read)、編集・更新(Update)、削除(Delete)を一通り実装。
  3. pytest によるテストコード作成
    • Flask の test_client を使い、テスト実行ごとにテーブルをクリーンアップして検証する仕組みを構築。
  4. GitHub Actions による CI(自動テスト)化
    • GitHub 上で git push するたびに、PostgreSQL サービスコンテナを起動して pytest を自動実行する環境を作成。

🛠️ 主な実装コード

テストコード(test_app.py)

import pytest
from app import app
from db import DATABASE_URL
from init_db import init_db
import psycopg2

@pytest.fixture
def client():
    app.config["TESTING"] = True
    
    # テーブルが存在しない場合は自動作成
    init_db()

    with app.test_client() as client:
        # テスト実行前にテーブルを初期化
        conn = psycopg2.connect(DATABASE_URL)
        cur = conn.cursor()
        cur.execute("TRUNCATE TABLE books RESTART IDENTITY;")
        conn.commit()
        cur.close()
        conn.close()

        yield client

GitHub Actions ワークフロー(.github/workflows/test.yml)

name: Flask App CI Tests

on:
  push:
    branches: [ "main" ]

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: bookgram_db
          POSTGRES_PASSWORD: postgrespassword
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - name: Check out repository code
      uses: actions/checkout@v4

    - name: Set up Python 3.12
      uses: actions/setup-python@v5
      with:
        python-version: "3.12"

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi

    - name: Run Pytest
      env:
        DATABASE_URL: postgresql://postgres:postgrespassword@localhost:5432/bookgram_db
      run: |
        pytest test_app.py -v


💥 CI/CD構築で何度もエラーになった試行錯誤の全記録

CI環境を構築するにあたり、一発では成功せず何度も赤色のバツ印(FAILED)に悩まされました。実際に遭遇したエラーと解決までのアプローチです。

エラー1: PAT(Personal Access Token)の権限不足による git push 拒否

  • 症状: ワークフロー定義ファイル(.github/workflows/test.yml)を git push しようとした際、以下のエラーでリモートから拒否された。
! [remote rejected] main -> main (refusing to allow a Personal Access Token to create or update workflow `.github/workflows/test.yml` without `workflow` scope)

  • 原因: GitHubのPersonal Access Token(PAT)に、Actionsのワークフローファイルを書き換えるための workflow スコープが付与されていなかった。
  • 解決法: GitHubの Settings ➔ Developer settings ➔ Personal access tokens を開き、対象トークンのスコープ設定で workflow にチェックを入れて更新した。

エラー2: モジュール読み込み失敗(ImportError: cannot import name ...)

  • 症状: GitHub Actions上で pytest 実行時に Process completed with exit code 2. で失敗。
E ImportError: cannot import name 'DATABASE_URL' from 'db'
E ImportError: cannot import name 'init_db' from 'init_db'

  • 原因: CI環境でスクリプトが読み込まれた際、db.py や init_db.py 側での関数・変数の export/import 名が呼び出し元(app.py や test_app.py)と一致していなかった。
  • 解決法:
  • db.py 内で os.getenv("DATABASE_URL") を使って環境変数を正しく保持・エクスポートする形に修正。
  • init_db.py 内に def init_db(): を明示的に関数として定義し、外部からインポートできるように整理した。

エラー3: DBテーブルが存在しない(UndefinedTable: relation "books" does not exist)

  • 症状: モジュール読み込みは成功したものの、テストのセットアップ段階でエラーが発生。
psycopg2.errors.UndefinedTable: relation "books" does not exist

  • 原因: GitHub Actions上でPostgreSQLコンテナは起動していたが、pytest 実行時にコンテナ内のデータベースに books テーブルがまだ作成されていなかった。
  • 解決法: test_app.py の pytest フィクスチャ内で、テスト前に必ず init_db()(CREATE TABLE IF NOT EXISTS)を実行させるように変更し、テーブルがなければ自動構築される堅牢な構造にした。

エラー4: IDE(PyCharm)での psycopg2 インポートエラー

  • 症状: ターミナルでのテストは通るものの、PyCharm上で import psycopg2 に赤波線が表示され Unresolved reference になる。
  • 原因: PyCharmのプロジェクト設定(Python Interpreter)が、パッケージをインストールした仮想環境(venv)ではなくグローバル環境を参照していた。
  • 解決法: PyCharmの Settings ➔ Project: book-log-app ➔ Python Interpreter で、venv/bin/python を明示的に選択してパスを通した。

まとめと感想

ハンズオンを終えて終わりにせず、自分で応用アプリを作成し、CI/CD環境まで構築したことで多くの学びがありました。

特にCI環境構築では「ローカルで動くのにクラウド上で動かない」という壁に何度もぶつかりましたが、エラーログを一つずつ紐解いて設定やコードを見直すことで、緑色のチェックマーク(PASSED)を表示させることができました。

次はコードの保守性を高めるため、HTMLテンプレートの分離(templates/)や検索機能の実装に挑戦したいと思います!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?