きっかけ
GitHubActionsを最近学んでいます。もしGitHubActionsで自動テスト動作する環境を作るためには、どういう感じに書くのだろうか、と思い作ってみました。
実施した内容
- CIの自動テストをGitHubActionsで動かす
- 主なライブラリはPytestとLocalStackを使う
- 疑似プロダクションコード(main.py)は、S3にオブジェクトをを作成する
- テストコードでは(test_main,py)は、指定したオブジェクト名がS3に存在するかテストする
name: localstack-ci-example
on: pull_request
jobs:
ci-example:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: start LocalStack
run: docker-compose up -d localstack
- name: Wait for LocalStack to start up for up to 60 seconds
run: "end_time=$(( $(date +%s)+60));
while [[ $(curl -s localhost:4566 | jq -r '.status') != 'running' ]];do
[[ $(date +%s) > ${end_time} ]] && exit 1 || sleep 1;
done"
- run: pipx install pipenv
- uses: actions/setup-python@v3
with:
python-version: '3.10'
- run: pipenv install --dev
- name: Execute unit test
run: pipenv run pytest tests/
docker-compose.yml
version: '3.8'
services:
localstack:
image: localstack/localstack:latest
environment:
- SERVICES=s3
- DEFAULT_REGION=ap-northeast-1
- TZ=Asia/Tokyo
ports:
- 4566:4566
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
boto3 = "*"
[dev-packages]
pytest = "*"
pytest-mock = "*"
[requires]
python_version = "3.10"
main.py
import boto3
from datetime import datetime as dt
s3 = boto3.resource(
's3'
)
def main():
# ファイル名はカレント日時
upload_object(dt.now().strftime('%Y%m%d%H%M%S'))
def upload_object(obj_name: str) -> None:
bucket = s3.Bucket('test-bucket')
bucket.Object(obj_name).put()
if __name__ == '__main__':
main()
test_main.py
import pytest
import boto3
import botocore
import os
import sys
from datetime import datetime as dt
currrent_path = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.join(currrent_path, '../'))
import main
s3 = boto3.resource(
's3',
endpoint_url='http://localhost:4566',
aws_access_key_id='DEFAULT_ACCESS_KEY',
aws_secret_access_key='DEFAULT_SECRET'
)
obj_name :str = 'test_file_' + dt.now().strftime('%Y%m%d%H%M%S')
@pytest.fixture(scope='session', autouse=True)
def setup_s3():
try:
s3.create_bucket(
Bucket='test-bucket',
CreateBucketConfiguration={'LocationConstraint': 'ap-northeast-1'}
)
except s3.meta.client.exceptions.BucketAlreadyOwnedByYou:
# バケットがすでに存在するなら何もしない
pass
def test_uploead_object(mocker):
# 向き先をlocalstackに変更
mocker.patch('main.s3', s3)
# テスト対象呼び出し
main.upload_object(obj_name)
bucket = s3.Bucket('test-bucket')
# S3にファイルが存在するかassert
ls_result = bucket.meta.client.list_objects_v2(Bucket=bucket.name, Prefix=obj_name)
assert ls_result.get('Contents')
# 後片付け
bucket.Object(obj_name).delete()
おわりに
GiHubActionsで動かすならこういうことをやるんだろうな、と想像しながら書いてみました。
実際に使うときは、pipenv install
、LocalStackのpullとup
は時間が掛かるので、cacheを使用したほうが良さそうですね。
参考リンク
https://dev.classmethod.jp/articles/localstack-on-github-actions-sample/
https://note.com/dd_techblog/n/n75bffb92874d
https://zenn.dev/alivelimb/articles/20220508-aws-python-testing