【AWS】Lambdaをローカル環境でテストするのにSAMを使う
ローカル環境でLambdaをテストするには「AWS SAM」を使います。Lambdaのテスト方法は主には以下の2択です。
- 本物のLambda関数でテスト
- ローカル環境でSAMでテスト
本物のLambda関数をテストしたくない場合、ローカル環境でSAMの方を選択することになります。本記事ではローカル環境でSAMを使用し、Lambda関数をテストする方法を紹介します。
[1] AWS SAMのインストール
SAMのインストール方法とチュートリアルの実行例は下記記事で紹介しています。
https://predora005.hatenablog.com/entry/2021/06/01/190000predora005.hatenablog.com
[2] プロジェクトの作成
sam init
でプロジェクトを作成します。ここでは、Python3.8
のHello, Worldプロジェクトを例に説明していきます。詳細は別記事で紹介しています。
$ sam init ...(途中省略)... ----------------------- Generating application: ----------------------- Name: sam-app Runtime: python3.8 Dependency Manager: pip Application Template: hello-world Output Directory: .
sam init
は対話形式ですが、以下のようにパラメータを指定することも可能です。
# パラメータを指定 $ sam init --name sam-app --runtime nodejs10.x --dependency-manager npm --app-template hello-world $ sam init --name sam-app --package-type image --base-image nodejs10.x-bas # GitHubに保存したテンプレートから作成 $ sam init --location gh:aws-samples/cookiecutter-aws-sam-python $ sam init --location git+ssh://git@github.com/aws-samples/cookiecutter-aws-sam-python.git $ sam init --location hg+ssh://hg@bitbucket.org/repo/template-name # zip化したテンプレートから作成 $ sam init --location /path/to/template.zip $ sam init --location https://example.com/path/to/template.zip # ローカルに保存されたテンプレートから作成 $ sam init --location /path/to/template/folder
[3] ビルド
sam build
でビルドします。
$ cd sam-app/ $ sam build Building codeuri: /home/ec2-user/sam-app/hello_world runtime: python3.8 metadata: {} functions: ['HelloWorldFunction'] Running PythonPipBuilder:ResolveDependencies Running PythonPipBuilder:CopySource Build Succeeded Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml Commands you can use next ========================= [*] Invoke Function: sam local invoke [*] Deploy: sam deploy --guided
[4] テスト
[4-1] 関数を直接呼び出し
sam local invoke
で関数をローカルで呼び出せます。
$ sam local invoke Invoking app.lambda_handler (python3.8) Image was not found. Building image........................ Skip pulling image and use local one: amazon/aws-sam-cli-emulation-image-python3.8:rapid-1.23.0. Mounting /home/ec2-user/sam-app/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container END RequestId: e3e79ef0-1db0-4713-a3bb-434dd4022e4f REPORT RequestId: e3e79ef0-1db0-4713-a3bb-434dd4022e4f Init Duration: 0.34 ms Duration: 307.81 mBilled Duration: 400 ms Memory Size: 128 MB Max Memory Used: 128 MB {"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
-e
オプションで、イベントファイルを読み込ませることも可能です。
$ sam local invoke "HelloWorldFunction" -e events/event.json
[4-2] API Gatewayをローカルで起動
sam local start-api
で、API Gatewayのローカルインスタンスを起動できます。
$ sam local start-api Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2021-05-01 14:38:53 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
表示されたURL(エンドポイント)にアクセスすると、応答が返ってきます。
$ curl http://127.0.0.1:3000/hello {"message": "hello world"}
[4-3] Lambda関数のエンドポイントをローカルに作成
[4-3-1] 実行方法
sam local start-lambda
でLambda関数のエンドポイントを作成できます。単発でテストする分にはsam local invoke
でも問題ありませんが、AWS SDKを用いてプログラムから起動する場合などで有効活用できます。
$ sam local start-lambda Starting the Local Lambda Service. You can now invoke your Lambda Functions defined in your template through the endpoint. 2021-05-01 14:39:41 * Running on http://127.0.0.1:3001/ (Press CTRL+C to quit)
aws lambda invoke
を使い、エンドポイント経由で起動できます。
$ aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --payload file://events/event.json response.json { "StatusCode": 200 }
[4-3-2] コマンドのオプション
aws lambda invoke
の主なオプションは次の通りです。
オプション | 内容 |
---|---|
function-name | 関数名 |
endpoint-url | エンドポイント |
invocation-type | RequestResponse ,Event , DryRun のいずれか。RequestResponse は同期的呼出、Event は非同期呼出。 |
payload | Lambda関数へ渡す入力 |
log-type | Tail でLogResult を出力、None で出力無し。ローカルでは Tail は使えないよう。 |
no-verify-ssl | SSL 証明書認証をスキップ |
詳細は公式のリファレンスに記載があります。
invoke — AWS CLI 1.19.62 Command Reference
sam local start-lambda
のオプションについても公式に一覧が載っています。
sam local start-lambda - AWS Serverless Application Model
[4-4] サンプルイベントを使う
sam local generate-event
を使うと、サンプルイベントを作成してくれます。help
オプションでイベントの種類が確認できます。
$ sam local generate-event --help ...(途中省略)... Commands: alexa-skills-kit alexa-smart-home apigateway appsync batch cloudformation cloudfront cloudwatch codecommit codepipeline cognito config connect dynamodb kinesis lex rekognition s3 sagemaker ses sns sqs stepfunctions
以下はAPI Gateway(apigateway)の例です。まずはヘルプで使い方を確認します。
$ sam local generate-event apigateway -h Usage: sam local generate-event apigateway [OPTIONS] COMMAND [ARGS]... Options: -h, --help Show this message and exit. Commands: authorizer Generates an Amazon API Gateway Authorizer Event aws-proxy Generates an Amazon API Gateway AWS Proxy Event
イベントを出力し、出力したイベントをLambda関数に渡すことができます。
$ sam local generate-event apigateway aws-proxy > events/apigateway-event.json $ sam local invoke "HelloWorldFunction" -e events/apigateway-event.json
[4-5] より詳細を知りたい場合は
公式(下記)を参照してください。
[5] ユニットテスト(pytest)
AWS SAMの機能ではありませんが、Hello, Worldテンプレートにユニットテスト用のソースが格納されています。pytest
で実行すると以下のようになります。
$ pytest tests/unit/ ======================================= test session starts ======================================= platform linux -- Python 3.8.5, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 rootdir: /home/ec2-user/sam-app plugins: mock-3.6.0 collected 1 item tests/unit/test_handler.py . [100%] ======================================== 1 passed in 0.03s ========================================
pytest
のインストールはpip
で可能です。
$ pip3 install -U pytest pytest-mock --user
Installation and Getting Started — pytest documentation
終わりに
「SAM」は「Cloud Formation」と似た機能(公式は拡張機能と言っている)であり、学習コストがかかるのも「CloudFormation」と同じです。しかし、「CloudFormation」は現在フル活用している程に便利な機能なので、SAMも少しずつ使いこなせるようにしたいと思います。
参考文献
出典
補足
[補足1] ローカルエンドポイントに対してAWS SDKから呼出
AWS SDK for Python (Boto3) のインストールはpip
コマンドで行います。
$ pip3 install boto3 --user
下記公式に掲載されているソースコードをそのまま実行すると、次の結果が得られます。
$ python3 lambda-endpoint.py Traceback (most recent call last): File "lambda-endpoint.py", line 30, in <module> assert response == "Hello World" AssertionError
response
は"Hello World"ではないので、この動作は正常です。assert
で例外を出さないためには、数行の追加とassert
の行を変更が必要です。
import json ...(途中省略)... payload = json.load(response['Payload']) body = json.loads(payload['body']) # (変更前) assert response == "Hello World" assert body['message'] == "hello world"
Integrating with automated tests - AWS Serverless Application Model
[補足2] デプロイ時に生成されるsamconfig.toml
version = 0.1 [default] [default.deploy] [default.deploy.parameters] stack_name = "sam-app" s3_bucket = "aws-sam-cli-managed-default-samclisourcebucket-xgx2dia4ru2p" s3_prefix = "sam-app" region = "ap-northeast-1" confirm_changeset = true capabilities = "CAPABILITY_IAM"
[補足3] パッケージ
作成したアプリをパッケージ化してS3バケットにアップロードします。
$ sam package --template-file template.yaml --s3-bucket {バケット名} \ --s3-prefix hello_world --output-template-file template_packaged.yaml
--s3-prefix
はzipを直接バケット直下に置いてもいい場合は指定不要です。上の例ではhello_world
フォルダ下にzipが格納されます。
--output-template-file
には変更後のテンプレート出力先をセットします。テンプレート内のCodeUri
がzipが格納されたS3のURLに変更されています。
このパッケージを使って、デプロイが可能になります。
$ sam deploy --template-file /home/ec2-user/sam-app/package_template.yaml --stack-name <YOUR STACK NAME>