LocalStack で S3・SNS のテストを書いてみた
はじめに
ポートでPHPエンジニアをしている @sawada です。
先日、担当しているプロジェクトにて、localstack を導入しましたので、そちらについて書いていきたいと思います。
localstack について
localstack は、AWS のクラウドサービスシュミレータです。
ローカル開発環境、CI 環境にて、AWSと同等の環境を構築することが可能となり、より本番に近い環境での開発、テストが可能となります。
動作確認の為に、実際にAWSへデプロイする必要も無くなるので、コスト削減にも繋がります。
また、Docker イメージも提供されているのでプロダクトへの導入も非常に簡単です。
全ての機能を利用することはできませんが、非常に多くの機能が提供されています。
実装機能の詳細は、こちらをご確認下さい。
Community(無償)版・Pro(有償)版の違い
localstack には、Community (無償)版と Pro (有償)版が存在します。
Community 版と Pro 版で色々と異なりますが、特に注意しておきたい事は以下の2点です。
- Community 版はデータが永続化されない
- 利用できるサービス・API が制限される
Community 版はデータが永続化されない
Community 版ではデータの永続化が行われません。
Docker を使用している場合は、コンテナが落ちた段階で設定データ含め、全て初期状態に戻ってしまうので注意が必要です。
初期設定の操作が多く、煩雑な場合は shell にまとめる、などしておくと無難でしょう。
利用できるサービス・API が制限される
Community版では、一部のサービス・APIのみ利用可能となっています。
今回はアプリケーション側から使用されることが多いであろう主要なサービスのみ表にまとめました。
サービス | Community版・Pro版 | 備考 |
---|---|---|
S3 | Community版から利用可能 | 提供されている機能は全て利用可能 |
SNS | Community版から利用可能 | 提供されている機能は全て利用可能 |
SQS | Community版から利用可能 | 提供されている機能は全て利用可能 |
Lambda | Community版から利用可能 | 提供されている機能は全て利用可能 |
API Gateway | Community版から使用可能 | 一部 API は Pro版のみ |
DynamoDB | Community版から使用可能 | 一部 API は Pro版のみ |
ElastiCache | Pro版のみ | - |
Cognito | Pro版のみ | - |
全てのサービス・API を確認する場合は下記をご確認下さい。
(下記のページにて、(Pro)
と記述されたサービス・API が Pro版でのみ使用可能となります。)
使ってみた
では、早速使っていきたいと思います。
今回はPHPアプリケーションで、S3・SNSを利用しているものを想定し、各種設定方法、テストコードを記述していきます。
localstack の設定
localstack を使用する場合は、ローカルマシンにインストールするか、Docker を利用することとなります。
今回は、Community版を Docker を利用して構築していきたいと思います。
docker-compose.yml
は、以下の通りです。
version: '3'
services:
app:
image: php:latest
ports:
- 8080:80
volumes:
- './:/app'
tty: true
working_dir: '/app'
localstack:
image: localstack/localstack:2.0.2
ports:
- 4566:4566
environment:
SERVICES: 'S3,SNS'
AWS_ACCESS_KEY_ID: "localstack-test"
AWS_SECRET_ACCESS_KEY: "localstack-test"
AWS_DEFAULT_REGION: "ap-northeast-1"
今回は、ポート 4566 のみ公開しています。
(以前のバージョンでは、ポート番号毎にサービスが割り当てられていたようですが、現状は単一のポートから全て利用できるようになったみたいです。)
次に、localstack コンテナ内の aws cli を利用し、S3, SNS の各種設定を行っていきます。
S3 の設定
aws cli を利用し、テスト用のS3バケットを作成します。
このとき、--endpoint-url
として、localstack のエンドポイントを指定する必要があります。
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
s3 mb s3://test-bucket
以下のコマンドで、バケット一覧を確認できます。
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
s3 ls
S3バケットが作成されたことが確認できれば完了です。
SNS の設定
同じく、aws cli を利用し、
- テスト用のトピック
- プラットフォームアプリケーション(ios, android 共に)
- プラットフォームエンドポイント(ios, androind 共に)
を作成します。
s3 の時と同様に--endpoint-url
を指定します。
トピックの作成
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
sns create-topic --name localstack-test
トピック一覧の確認
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
sns list-topics
プラットフォームアプリケーション
docker compose exec localstack aws --endpoint=http://localhost:4566 \
sns create-platform-application --name app-test --platform APNS --attributes {}
docker compose exec localstack aws --endpoint=http://localhost:4566 \
sns create-platform-application --name app-test --platform GCM --attributes {}
プラットフォームアプリケーション一覧の確認
docker compose exec localstack aws --endpoint=http://localhost:4566 sns list-platform-applications
プラットフォームエンドポイント
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
sns create-platform-endpoint \
--platform-application-arn "arn:aws:sns:ap-northeast-1:000000000000:app/APNS/app-test" --token my-fake-token-apns
docker compose exec localstack aws --endpoint-url=http://localhost:4566 \
sns create-platform-endpoint \
--platform-application-arn "arn:aws:sns:ap-northeast-1:000000000000:app/GCM/app-test" --token my-fake-token-gcm
プラットフォームエンドポイント確認
docker compose exec localstack aws --endpoint-url=http://localstack:4566 \
sns list-endpoints-by-platform-application \
--platform-application-arn="arn:aws:sns:ap-northeast-1:000000000000:app/APNS/app-test"
docker compose exec localstack aws --endpoint-url=http://localstack:4566 \
sns list-endpoints-by-platform-application \
--platform-application-arn="arn:aws:sns:ap-northeast-1:000000000000:app/GCM/app-test"
SNS の設定は以上で完了となります。
なお、localstack の提供している awsLocal を使用すると--endpoint-url
の記述を省くことができますが、今回は長くなるので割愛します。
詳細は、以下をご確認下さい。
次に、PHPアプリケーション側のAWSのクライアントの設定を行います。
PHPアプリケーションの AWS コンフィグの設定
各種 AWSClint 作成時に必要な値を設定していきます。
// 本番環境用
$config = [
'credentials' => new \Aws\Credentials\Credentials(
'...',
'...'
),
'region' => 'ap-northeast-1',
'version' => 'latest',
];
// localstack用
$config = [
'credentials' => new \Aws\Credentials\Credentials(
'localstack-test',
'localstack-test'
),
'region' => 'ap-northeast-1',
'version' => 'latest',
'endpoint' => 'http://localstack:4566',
'use_path_style_endpoint' => true, // endpoint を path 形式とする
];
本番環境とlocalstack環境との違いは、
- endpoint の指定
- パススタイルのエンドポイントを使用するようにしている
という2点です。
引数の詳細は、以下から確認して下さい。
次は、テストコードを書いていきます。
S3 に依存するクラスのテストコード
ImageService
というクラスが存在し、save
メソッドの呼び出しで引数に指定したパスの画像が S3 に保存される、という事を想定し、
それを確認するテストコードを書いていきます。
public function test_save()
{
// 準備
$dummyFilePath = $this->createDummyFile(); // テストファイルを作成
$dummyFileContents = get_file_contents($dummyFilePath);
$config = $this->get_config(); // テスト用の config を取得する
$s3Client = new S3Client($config);
$sut = new ImageServise();
// 実行
$result = $sut->save($testFile);
// 検証
$s3Object = $s3Client->getObject([
'Bucket' => 'test-bucket',
'Key' => $result['key'],
]);
$this->assertEquals($dummyFileContents, $s3Object['Body']);
}
テスト対象メソッド呼び出し後、s3クライアントでS3オブジェクトを取得できること、取得したコンテンツが保存したものと同様であることを確認しています。
SNS に依存するクラスのテストコード
SNSService
というクラスが存在し、 subscribe
メソッドの呼び出しで、endpointが有効化される、という事を想定し、
それを確認するテストコードを書いてきます。
public function test_subscribe()
{
$config = $this->get_config(); // テスト用の config を取得する
$snsClient = new SnsClient($config);
$testDeviceToken = uniqid();
$sut = new SNSService();
// 実行
$result = $sut->subscribe($testDeviceToken);
// 検証
$snsEndpoint = $snsClient->getEndpointAttributes([
'EndpointArn' => $result->endpointArn,
]);
$this->assertEquals('true', $snsEndpoint['Attributes']['Enabled']);
}
こちらも、テスト対象メソッド呼び出し後、SNSクライアントから endpoint を取得し、有効化されているか(getEndpointAttributes
の戻り値のEnabled
が'true'
かどうか)確認しています。
最後に
以上となります。 いかがでしたでしょうか?
今回は、localstack を使用し、S3, SNS に依存するクラスのテストを書いてみました。
localstack を使用することで、より本番環境に近い状態で開発・テストを行え、開発体験も向上すると思われます。
今後も積極的に利用していきたいと思っています!
ご参考になれば幸いです。
参考
Discussion