💽

Github ActionsでMySQLに接続してDjango REST Frameworkのunittestを実行したい

2021/06/12に公開

背景

Django REST FrameworkでAPIサーバーを作成したが、CI環境を作ったことが無かったのでGithub Actionsでunittestを実行させてみた。
その際にデータベースとの接続でかなりつまずいたので、まとめておきます。
(他にも沢山苦労しましたが、Github Actionsが関連するポイントとして上記をまとめようと思いました)

本題

では具体的に引っかかった点と解決方法を説明します。

Github Actions上におけるDatabaseとの接続方法

今回のケースでは、以下のようなdocker-compose.ymlを用意し、DjangoアプリケーションとMySQLを接続していました。

version: "3"
services:
  web:
    container_name: django-container
    image: python:3.9.1
    working_dir: /code
    ports:
      - 8000:8000
    volumes:
      - .:/code
    tty: true
    depends_on:
      - db
  db:
    container_name: mysql-container
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: test-db
      MYSQL_ROOT_USER: test-user
      MYSQL_ROOT_PASSWORD: example
    ports:
      - 3306:3306

また、テストコードは以下のようにviewの振る舞いを確認する内容なっており、MySQLに格納されるデータを参照するようになっています。

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework_simplejwt.tokens import RefreshToken

from api.tests.factory import UserFactory, DrinkFactory

class DrinkModelViewSetTestCase(APITestCase):
    # Drinkを正常に作成(POST)できることを確認
    def test_status_code_when_post_drink(self):
        # 認証に必要なtokenをheaderにセットする
	user = UserFactory()
        token = str(RefreshToken.for_user(user).access_token)
        self.client.credentials(HTTP_AUTHORIZATION='JWT ' + token)
        
	# テスト対象のURLを取得
        url = reverse('drink-list')
	# POSTするデータ
        data = {
            'drink_name': 'Water',
            'price': 100,
        }
        response = self.client.post(url, data, format='json')
        
	# POSTした結果のstatus_codeが201であることを確認
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        

また、Github Actionsでデフォルトで用意されているDjangoのworkflowであるdjango.ymlは以下の内容になっています。

name: Django CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      max-parallel: 4
      matrix:
        python-version: [3.6, 3.7, 3.8]

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v2
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install Dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run Tests
      run: |
        python manage.py test

このworkflowをそのまま設定すると、Githubの仮想環境上ではMySQLが起動しておらず、接続ができないため失敗します。

そのためGithub上でMySQLを起動してあげる必要があります。

解決策

django.ymlInstall Dependenciesの直前に以下のstepを追加しました。

# Github上にdockerコンテナを起動させる
- name: Docker Container Create
        run: |
          docker-compose up -d
          docker-compose up -d
          sleep 20s
	  # コンテナが起動しているかログに出力する
          docker-compose ps
            # MySQLが8.0なのでユーザーの認証方法を変更する
      - name: Set up Mysql
        run: |
          docker exec -t mysql-container mysql -u test-user -pexample -e"ALTER USER 'test-user'@'%' IDENTIFIED WITH mysql_native_password BY 'example';"

Github上でMySQLのコンテナを立ち上げることで、接続可能なデータベースを用意することができます。

また、pythonのコマンドもコンテナ内で実行するように修正します。

    - name: Install Dependencies
      run: |
        docker exec -t django-container python -m pip install --upgrade pip
        docker exec -t django-container pip install -r requirements.txt
    - name: Run Tests
      run: |
        docker exec -t django-container python manage.py test

これでGithub上でもMySQLに接続し、ユニットテストが通るようになるはずです!

まとめ

  • Github上にMySQLのDockerコンテナを作成して上げるとデータベースとも接続できる

※もっと効率的な方法をご存知の方がいらっしゃったらご指摘いただけますと幸いです

Discussion