はじめに
AWS SAMについてローカルで実行可能なテスト方法について学習したので内容を整理します。
今回は「sam init で作成したテンプレートをベースにSAMのローカルテストを実行してみる」を目標とします。
前提条件
ローカルでテストを実施するにあたって前提条件は以下の通りです。
- AWS CLIインストールしていること
- AWS SAM CLIインストールしていること
- dockerコマンドの実行やコンテナ構築が可能なこと
また、今回の説明ではPython3.13を使用します。
AWS SAMとは
AWS公式ドキュメントより抜粋
AWS Serverless Application Model (AWS SAM) は、Infrastructure as Code (IaC) を使用した、サーバーレスアプリケーション構築のためのオープンソースのフレームワークです。
AWS SAM の省略構文を使用して、デベロッパーは、デプロイ中にインフラストラクチャに変換される AWS CloudFormation リソースおよび特殊なサーバーレスリソースを宣言します。
今回の実施内容
今回は下記の3点を実施していきます。
通常であれば、テストの後にAWS環境へデプロイを行うのですが今回は省略します。
テストの対象となるプロジェクトはシンプルにAmazon API GatewayとAWS Lambdaの構成として、
GETメソッドのリクエストに対して、ステータスコード、ボディにJSON形式でメッセージを返すごく単純なものとします。
- プロジェクトを作成
今回使用するプロジェクトを作成します。 - ローカルでサーバーを立ち上げてAPIテストを実施してみる
sam local start-api
コマンドでサーバーを立ち上げてテストを行います。 - Lambda 関数を直接呼び出してテストしてみる
sam local invoke
コマンドでAWS Lambdaの関数を呼び出してテストを行います。
プロジェクトを作成
使用するプロジェクトはsam init
コマンドでAWSのテンプレートを基に作成します。
プロジェクトを作成するディレクトリへ移動し、sam initコマンドを実行します。
sam init
コマンド実行後に作成するプロジェクトのテンプレートについて質問が表示されます。
作成するテンプレートは以下のように選択してください。
- 1 – AWS Quick Start Templates
- 1 – Hello World Example
- python3.13
- 1 – Zip
次に、下記の質問からオプションの有無についての質問が開始されます。
こちらはデフォルト(N)で問題ありません。
Based on your selections, the only dependency manager available is pip. We will proceed copying the template using pip. ・・・省略・・・
作成するプロジェクト名は任意で構いません。
デフォルトのsam-app
としておきます。
Project name [sam-app]:
作成されたテンプレートのtemplate.yaml
からは使用しているAWSサービスの構成内容を確認し、
hello_world/app.py
からはLambdaのレスポンス内容を確認してみます。
なお、本稿ではコメントアウト部分を除外していますが、内容はテンプレートで作成されるものと同じです。
template.yaml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: > sam-app Sample SAM Template for sam-app Globals: Function: Timeout: 3 Resources: HelloWorldFunction: Type: AWS::Serverless::Function Properties: CodeUri: hello_world/ Handler: app.lambda_handler Runtime: python3.13 Architectures: - x86_64 Events: HelloWorld: Type: Api Properties: Path: /hello Method: get Outputs: HelloWorldApi: Description: "API Gateway endpoint URL for Prod stage for Hello World function" Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/" HelloWorldFunction: Description: "Hello World Lambda Function ARN" Value: !GetAtt HelloWorldFunction.Arn HelloWorldFunctionIamRole: Description: "Implicit IAM Role created for Hello World function" Value: !GetAtt HelloWorldFunctionRole.Arn
hello_world/app.py
import json def lambda_handler(event, context): return { "statusCode": 200, "body": json.dumps({ "message": "hello world", }), }
プロジェクトの準備が整えば、
sam validate
コマンドで実装ミスがないかチェックをした後、sam build
でビルドを行います。
sam build
の実行後に.aws-sam
が作成されることを確認してください。
sam validate sam build
これ以降のテストで実施するコードはsam build
で生成されたコードを使用します。
もしも、挙動がおかしいと感じたらsam build
を実行してみてください。
ローカルでサーバーを立ち上げてAPIテストを実施してみる
sam local start-api
コマンドを使用することで、
API GatewayとLambdaの構成をローカル環境で再現します。
この再現されたローカル環境を使用して、APIリクエストを通じてLambda関数の挙動を確認できるため、
ある程度、本番環境に近い形でのデバッグや開発が可能になります。
プロジェクト直下ディレクトリでローカル環境を立ち上げます。
sam local start-api
ローカル環境が立ち上がると下記のようにホストURLが確認でき、
そのままログを確認できるようになります。
* Running on http://127.0.0.1:3000 2025-03-14 00:00:00 Press CTRL+C to quit
ブラウザで次のURLを開くか、ターミナルで curl コマンドを実行することで、APIリクエストを送信し、レスポンスを確認できます。
http://127.0.0.1:3000/hello
以下では、curlコマンドに-iオプションをつけてステータスコードを確認できるようにします。
このレスポンスでステータスコード 200 OK と JSONレスポンス が返ってくれば、正常に動作していることが確認できます。
curl -i http://localhost:3000/hello HTTP/1.1 200 OK Server: Werkzeug/3.0.6 Python/3.11.10 Date: Fri, 14 Mar 2025 00:00:00 GMT Content-Type: application/json Content-Length: 26 Connection: close {"message": "hello world"}
この時、立ち上げたローカル環境のログから次のようなログを合わせて確認することもできます。
[14/Mar/2025 00:00:00] "GET /hello HTTP/1.1" 200 -
Lambda 関数を直接呼び出してテストしてみる
sam local invoke
コマンドを使用することでLambda関数をローカル環境で直接実行し、その動作を確認できます。
コマンドを実行する際に-e
または--event
でJSONファイルパスを指定することでLambdaに渡すイベントデータを設定することが可能です。
今回作成したプロジェクトテンプレート中のevents/event.json
を使用してみます。
プロジェクト直下ディレクトリへ移動し、Lambda関数を実行します。
sam local invoke HelloWorldFunction --event events/event.json
この実行結果でも先ほどと同じようにステータスコード 200 OK と JSONレスポンスが返ってくることが確認できます。
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
先ほどテンプレートにあらかじめ作成されたファイルを使用しましたが、
sam local generate-event
を使用すると、新しいJSONパラメータを生成できます。
このコマンドを使用すると、S3 や SQS などのさまざまなサービスのイベントを生成でき、
ローカル環境で各種サービスのイベントをLambda関数に渡し、その動作を再現できます。
なお、生成された JSON パラメータはターミナル上に標準出力されるため、
ファイルとして保存する場合は > 出力先ファイルパス
を指定します。
sam local generate-event > 出力先ファイルパス
今回のAmazon API GatewayとAWS Lambdaでの構成なので、
serviceはapigateway
、eventはaws-proxy
を指定しています。
sam local generate-event apigateway aws-proxy --method GET --path /hello --body '{}' > events/api-event.json
この生成結果は次のとおりとなります。
events/api-event.json
{ "body": "e30=", "resource": "/{proxy+}", "path": "//hello", "httpMethod": "GET", "isBase64Encoded": true, "queryStringParameters": { "foo": "bar" }, "multiValueQueryStringParameters": { "foo": [ "bar" ] }, "pathParameters": { "proxy": "//hello" }, "stageVariables": { "baz": "qux" }, "headers": { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, sdch", "Accept-Language": "en-US,en;q=0.8", "Cache-Control": "max-age=0", "CloudFront-Forwarded-Proto": "https", "CloudFront-Is-Desktop-Viewer": "true", "CloudFront-Is-Mobile-Viewer": "false", "CloudFront-Is-SmartTV-Viewer": "false", "CloudFront-Is-Tablet-Viewer": "false", "CloudFront-Viewer-Country": "US", "Host": "1234567890.execute-api.us-east-1.amazonaws.com", "Upgrade-Insecure-Requests": "1", "User-Agent": "Custom User Agent String", "Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==", "X-Forwarded-For": "127.0.0.1, 127.0.0.2", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Accept": [ "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8" ], "Accept-Encoding": [ "gzip, deflate, sdch" ], "Accept-Language": [ "en-US,en;q=0.8" ], "Cache-Control": [ "max-age=0" ], "CloudFront-Forwarded-Proto": [ "https" ], "CloudFront-Is-Desktop-Viewer": [ "true" ], "CloudFront-Is-Mobile-Viewer": [ "false" ], "CloudFront-Is-SmartTV-Viewer": [ "false" ], "CloudFront-Is-Tablet-Viewer": [ "false" ], "CloudFront-Viewer-Country": [ "US" ], "Host": [ "0123456789.execute-api.us-east-1.amazonaws.com" ], "Upgrade-Insecure-Requests": [ "1" ], "User-Agent": [ "Custom User Agent String" ], "Via": [ "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)" ], "X-Amz-Cf-Id": [ "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==" ], "X-Forwarded-For": [ "127.0.0.1, 127.0.0.2" ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "requestContext": { "accountId": "123456789012", "resourceId": "123456", "stage": "prod", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "requestTime": "09/Apr/2015:12:34:56 +0000", "requestTimeEpoch": 1428582896000, "identity": { "cognitoIdentityPoolId": null, "accountId": null, "cognitoIdentityId": null, "caller": null, "accessKey": null, "sourceIp": "127.0.0.1", "cognitoAuthenticationType": null, "cognitoAuthenticationProvider": null, "userArn": null, "userAgent": "Custom User Agent String", "user": null }, "path": "/prod//hello", "resourcePath": "/{proxy+}", "httpMethod": "GET", "apiId": "1234567890", "protocol": "HTTP/1.1" } }
生成したファイルは先ほどと同様に
sam local invoke HelloWorldFunction --event events/api-event.json
で実行でき、
{"statusCode": 200, "body": "{\"message\": \"hello world\"}"}
で結果を確認することができます。
まとめ
今回、AWS SAMのテスト機能を使用してみて、以下のような点が分かりました。
- Lambdaをコンソール上でデプロイ&テストする場合と比べて、手順を簡略化できる
- コストを抑えながらテストが可能
- 再現したいイベントの実行が簡単にできる
- ローカル環境は 「ある程度の再現」 にとどまるため、実際のAWS環境との違いを考慮してテストの範囲を明確にする必要がある
今回の内容を試しただけでも、AWS SAMにはさまざまな機能があることが分かりました。
さらに深掘りすることで、より多様な構成でのテストが可能になりそうです。