Edited at

circleciでAWSサービスを使ったテストをするには?

boto3がs3のサービスを使うAWSの接続なしでテストしたいですよね?


motoって?

そこでmotoを使ってきました。

(motoからの例)


import boto3
from moto import mock_s3
from mymodule import MyModel

@mock_s3
def test_my_model_save():
conn = boto3.resource('s3', region_name='us-east-1')
# We need to create the bucket since this is all in Moto's 'virtual' AWS account
conn.create_bucket(Bucket='mybucket')

model_instance = MyModel('steve', 'is awesome')
model_instance.save()

body = conn.Object('mybucket', 'steve').get()['Body'].read().decode("utf-8")

assert body == b'is awesome'

Decoratorだけで、サービスがモックされていて、シンプルでテスト書けますね。motoもs3だけじなくて、多くのサービスをサポートしています。


localstackって?

しかし、一昨年ぐらいから、localstack というmotoなどのパッケージをまとまって、完全にサーバとして動かせるAWS仮想環境が提供されてきました。

一応、motoも”Server Mode" (moto_server)がありますが、localstackのいいところは、docker imageを提供しています。大きな差がないのですが、個人的に開発環境に必要なmock(moto)をインストールしなくて済むことは多少すっきり感がします。

ただし、当然ですが、localstackのdockerを立ち上げるだけではすまないですね。現状のコードに工夫が必要になります。

いろいろな方法はあるかと思いますが、このように使っています:

(実際のコードですね。テストコードじゃない)

import os

import boto3

# reference:
# https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html
DEFAULT_BOTO3_S3_ENDPOINT = 'https://s3.ap-northeast-1.amazonaws.com'
BOTO3_S3_ENDPOINT = os.getenv('BOTO3_S3_ENDPOINT', DEFAULT_BOTO3_S3_ENDPOINT)
s3 = boto3.client('s3', endpoint_url=BOTO3_S3_ENDPOINT)

...

リジョンを跨ぐとめんどうだろうけど、これでロカールでテストしたい場合、BOTO3_S3_ENDPOINTの環境変数を指定だけでは済みます。

export BOTO3_S3_ENDPOINT=http://localhost:4572

これでこのようにlocalstackが起動されているなら、テストを実行して、AWSと同様にロカールで各サービスを使えます。

docker run --name development-localstack -d --rm -p 4567-4578:4567-4578 -p 8080:8080 localstack/localstack


circleciって?

circleciはCI環境で、そこにもAWSのようにテスト使いたいですよね。

そこでlocalstackでの導入が難しいかなと思ったんですが、以外とらくでした。

まず、dockerの定義配下でlocalstack/localstackのイメージを定義します。

(開発中のDjangoプロジェクトを一部を例として)


version: 2

jobs:
build:
working_directory: ~/erwin/
docker:
- image: circleci/python:3.6.5
environment:
PIPENV_VENV_IN_PROJECT: true
DJANGO_SETTINGS_MODULE: myproject.settings.circleci
- image: mdillon/postgis:9.6
environment:
POSTGRES_USER: circleci
POSTGRES_DB: circle_test
- image: localstack/localstack <---ここ!!!
...

次は、circleciのプロジェクト設定で、コード内で定義した環境変数をEnvironment Variables(環境変数)へ登録します。

BOTO3_S3_ENDPOINT=http://localhost:4572

以上

ポートどうなっているかなと思ったんですが、postgres/postgisのポート設定もないと気が付き、localstackも特別なポート設定なしで、プロジェクトのテストが無事にAWSの代わりにlocalstackを通信していて、テストが通りました。

嬉しい。

motoを使うかlocalstackを使うか正直にあまり変わらないですが、localstackでいいのかな?

次は、これをためしてみようかな: