はじめに
CRUDなWebアプリケーションを開発する際に、外部ストレージとしてAWS S3
を選定し、ファイルのアップロード処理やダウンロード処理などを実装する場合が多々あると思います。
AWS S3
のようなマネージドサービスに依存するビジネスロジックに関しても単体テストもしくは統合テストをきちんと書き、自動テストを実行したい!! といった場合に役立ちそうな内容を記事にまとめます。
選定したツール
今回はlocalstack
という、AWSのマネージドサービスをモッキングするためのツールを使用します。
localstack
を使用することで、AWSの多くのマネージドサービスをモッキングすることが可能となります。
ツールを利用する際に一部有料となってしまう部分も存在しますが、今回モッキングを行いたいS3に関しては利用に際して無料となっているので、特に問題なく開発が行えそうです。
選定理由
localstack
を選定するに至るまでいくつかの方法でS3をモックしてのテスト実行方法を模索してみましたがテスト環境の実装を進める中で不都合を感じたので断念しました。
interfaceを定義してmockする
AWSの公式ドキュメントAWS SDK for Go V2
にinterfaceを定義し、外部からDIすることでS3をmockする方法が紹介されていました。
s3のmock方法を検証し始めた当初、こちらの方針で実装を進めていましたが、テストコードを実装するためにプロダクションコードを複雑化させてしまっている
という点もあり、採用を断念しました。
AWSステージングアカウントのテスト用bucketを使用する
こちらは、AWSのステージング用アカウントにテスト用のS3bucketを作成し、テスト実行のたびに該当bucketに対してアップロード処理やダウンロード処理を実行するという方法です。
こちらの方法については、テストを実行するたびに、S3bucketのclean upができない
、AWSのリソースにアクセスするためのIAMユーザの作成&管理を行う必要があるため運用の手間が増える
という点から採用を断念しました。
S3をmockする際の大替案となる上記の選択肢の比較検討を踏まえて、以下の理由から最終的にlocalstack
を選定しました。
-
localstack
のdocker imageが公開されており、比較的容易にテスト環境の構築が行える。 - テスト実行のたびにテスト環境を一から構築するため、S3bucketのclean upが可能。
- S3のmock化のためにプロダクションコードを複雑化させるという懸念点が比較的少ない。
- 実際のAWSアカウントおよびリソースにアクセスする必要がないため、セキュリティ面での懸念点が少ない。
構成図
今回は、golang
で実装されたgRPC server
からのupload処理およびdownload処理を実行する際に外部ストーレージとして機能するAWS S3をモッキングします。
GitHub Actionsで自動テストを実行するための設定
GitHub Actions
で自動テストを実行するための設定ファイルを作成していきます。
設定ファイル完成版
name: gRPC Test
on:
push:
paths:
- "src/**"
defaults:
run:
shell: bash
working-directory: src/backend-go
env:
AWS_REGION: ap-northeast-1
AWS_END_POINT_URL: http://localhost:4566
AWS_S3_BUCKET: test-bucket
jobs:
test:
name: gRPC Test
runs-on: ubuntu-latest
services:
mysql:
image: mysql:5.7
env:
MYSQL_ROOT_PASSWORD: pass
MYSQL_DATABASE: test
ports:
- 3307:3306
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
localstack:
image: localstack/localstack
env:
SERVICES: s3
DEFAULT_REGION: ${{ env.AWS_REGION }}
DATA_DIR: /tmp/localstack/data
ports:
- 4566:4566
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: "1.18"
- name: Install dependences
run: go mod tidy
- name: Install Protoc
uses: arduino/setup-protoc@v1
with:
version: "3.19.4"
- name: Start LocalStack
run: |
pip install awscli-local[ver1]
awslocal s3 mb s3://${{ env.AWS_S3_BUCKET }} --endpoint-url=http://localhost:4566
awslocal s3 cp ./testdata/dummyFile.xls s3://${{ env.AWS_S3_BUCKET }}/ --endpoint-url=http://localhost:4566
- name: Make generate
run: |
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
export PATH="$PATH:$(go env GOPATH)/bin"
mkdir genproto
make generate
- name: Install migrate package
run: |
curl -L https://packagecloud.io/golang-migrate/migrate/gpgkey | sudo apt-key add -
echo "deb https://packagecloud.io/golang-migrate/migrate/ubuntu/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/migrate.list
sudo apt-get update
sudo apt-get install -y migrate
- name: Test database setup
run: |
mysql -h 127.0.0.1 --port 3307 -u root -D test -ppass < init.sql
migrate -path ./migrations -database $DATABASE_URL up
env:
DATABASE_URL: mysql://root:pass@tcp(localhost:3307)/test
- name: Run tests
run: make test
env:
AWS_S3_BUCKET: ${{ env.AWS_S3_BUCKET }}
AWS_ACCESS_KEY: localstack
AWS_SECRET_KEY: localstack
AWS_REGION: ${{ env.AWS_REGION }}
AWS_END_POINT_URL: ${{ env.AWS_END_POINT_URL }}
localstack
コンテナを起動する設定
jobs:
test:
name: gRPC Test
runs-on: ubuntu-latest
services:
:
:
localstack:
image: localstack/localstack
env:
SERVICES: s3
DEFAULT_REGION: ${{ env.AWS_REGION }}
DATA_DIR: /tmp/localstack/data
ports:
- 4566:4566
job
定義の中のserviceの部分にlocalstack
コンテナを起動するための設定を追記します。
ダミーbucketの作成&ダミーファイルのアップロード
- name: Start LocalStack
run: |
pip install awscli-local[ver1]
awslocal s3 mb s3://${{ env.AWS_S3_BUCKET }} --endpoint-url=http://localhost:4566
awslocal s3 cp ./testdata/dummyFile.xls s3://${{ env.AWS_S3_BUCKET }}/ --endpoint-url=http://localhost:4566
awslocal
コマンドを使用するために、pip
のinstall処理でawscli-local[ver1]
をインストールしましょう。
仮に実際のAWSリソースにアクセスする際と同様にaws
コマンドを使用したとしても、--endpoint-url
でlocal環境のエンドポイントを指定すれば、実際のAWSリソースにアクセスすることはないと思いますが、テスト環境ではawslocal
コマンドを使用してlocal環境のテスト用コンテナに対してコマンドが実行されるようにした方が安全なのかなと思います。
設定完了
上記の様に設定ファイルを作成しGitHubにpushすれば、AWS S3をmockした上でGitHub Actions
を用いて自動テストを実行することができるようになっていると思います。
最後に
AWSのマネージドサービスに依存するテストを実行するとなると、テスト環境構築の際に幾つかの手間が発生してしまいますが、今回のようにlocalstack
を用いることで容易にテスト環境を構築することが可能となりました。
S3以外のその他のマネージドサービスに依存するビジネスロジックの実装をおこなった際には、今回の様にlocalstack
の使用を検討したいと思います。
参考記事