ひぃ(hixi)の技術雑記ブログ

事実や解決策というよりも自分が思ったことをつらつらと書いていく所存。文章構成とかそういうのあまり気にせずに書きます

Go + AWS Service のテスト

python 用の AWS Service がテストできる GitHub - spulec/moto: A library that allows you to easily mock out tests based on AWS infrastructure. はとてもいいライブラリですね。

aws-cloudformation-lambda-ssm-put-parameter/test_index.py at a4aed513238354acd52f8d45fd9ef544ec8f2e7a · hixi-hyi/aws-cloudformation-lambda-ssm-put-parameter · GitHub みたくテストに mock とかくだけで、AWS のサービスを直接叩くことなく、ローカルで起動している AWS Service を利用してくれる。 とてもシンプルに簡単にテストを書かせてくれる。

さて、一方で Golang で AWS Service のテストはどのように書けばいいだろうか。 と調べると GitHub - localstack/localstack: 💻 A fully functional local AWS cloud stack. Develop and test your cloud & Serverless apps offline! という素晴らしいソリューションが出てくる(内部で moto も使ってる)

が、この localstack は言語非依存で AWS Service をローカルで起動するっていうもので、どの様な言語でも使えるものの、Go に最適化されているわけではない。 そのため Golang で愚直に使おうと思うと、if 文の分岐がプロダクションコードに挿入されることになる。 Lambda で言えば SAM LOCAL を使おうと思ったらそれ用の設定の IF 文。go test で使おうと思ったらそれ用の設定の IF 文。

普通に考えたらそんなの書きたくないから、どのようにか抽象化するっていうアプローチを取るとおもう。

ってことで、それを気にしなくて済むように GitHub - hixi-hyi/aws-client-go を作ってみた。

Lambda SAM LOCAL は残念ながらプロダクションと同じコードを用いてローカルにて実行するため、プロダクションコードに数行埋め込む必要があるが、テストコード用であればプロダクションコードに影響を与えることはない。

SAM LOCAL の場合のサンプルは GitHub - hixi-hyi/aws-client-go こんな感じ。Go SAM LOCAL の場合は Docker 上で動くため --docker-network $(docker network ls --filter "Name=localstack_default" --no-trunc -q) みたく、localstack docker とネットワーク的に疎通できるようにしておいて必要がある。 localstack の docker-compose の例はこんな感じ。

version: "3.3"

services:
  localstack:
    container_name: localstack
    image: localstack/localstack
    ports:
      - "4567-4600:4567-4600"
      - "8080:8080"
    environment:
      - SERVICES=sns
      - DEFAULT_REGION=ap-northeast-1
      - DOCKER_HOST=unix:///var/run/docker.sock
      - HOSTNAME_EXTERNAL=localstack
    networks:
      - localstack_default
networks:
  localstack_default:
    driver: bridge
    ipam:
      driver: default
sam local invoke Test --event test.json --region ap-northeast-1 --docker-network $(docker network ls --filter "Name=localstack_default" --no-trunc -q);

をすれば実行されます。

そして、 go test で利用する場合は、テストコード内で以下を呼び出しておけばいい。

awsclient.UseLocalStack(localstack.NewLocalStack())

※ この場合は localhost に対してのアクセスになる。 go test は docker を使ってない前提で。もちろん go test を docker を用いて実行するのであれば、 SAM LOCAL と一緒な感じ localstack などにアクセスするようにしてください。

awsclient の準備ができたら

    sess := session.Must(session.NewSession())
    client := awsclient.APIGateway(sess)

というように、必要なクライアントを受け取ればいい。AWS Service を使うのと LocalStack AWS Service を使うのに実装差分をなくすことができます。

サポートしているものは localstack のものだけになるので、それ以外の API を使う場合は自力でやんなきゃいけないところが難点ではあります。 ただ、結構サポートされているのと、 SNS / SQS などサポートされているので、Lambda で単純なものを書く場合や標準的な Web アプリケーションにおけるモックには十分なんじゃないかなっと。