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でいいのかな?

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