サービス開発課のくればやしです。
はじめに
AWSはAPIで各種リソースの操作が可能です。そのAPIを簡単に利用するため、各種プログラミング言語のSDKが提供されています。そのSDKの一つにRubyのSDKがあります(以下AWS SDK for Ruby)。
この記事ではAWS SDK for RubyでAPIレスポンスをスタブする際の基本をまとめてみようと思います。
環境の前提
- AWS SDK for Ruby v3
- Ruby 2.7.1
AWS SDK for Rubyの概要
AWS SDK for Rubyでは Aws::xxx::Client
のようなクライアント系のクラスを用いる方法と Aws::xxx::Resource
系のリソース系のクラスを用いる方法の大きく2通りの使用方法が存在します。以下簡単に両者の使い方を確認しておきます。
本記事では便宜的に前者をクライアント方式、後者をリソースインターフェース方式と記載します。
クライアント方式
クライアント方式はAPI単位でメソッドが提供されています。例えばEBSボリュームを取得する場合、 DescribeVolumes APIに対して describe_volumes
メソッドを呼び出すこととなります。
ec2_client = Aws::EC2::Client.new response = ec2_client.describe_volumes response.volumes.each do |volume| puts volume end
リソースインターフェース方式
リソースインターフェース方式では、各AWSのサービスを直感的に操作できるクラスが提供されています。上述のEBSボリュームの場合では以下のような形となります。
ec2 = Aws::EC2::Resource.new ec2.volumes.each do |volume| puts volume end
リソースインターフェース方式は内部的にクライアントのクラスをラップした形で実装されています。リソースインターフェース方式を使用する際、使用するクライアントのインスタンスを指定出来ます。
ec2_client = Aws::EC2::Client.new(region: "us-east-1") ec2 = Aws::EC2::Resource.new(client: ec2_client)
ただし、以下公式GitHubに記載があるようにリソースインターフェース方式は全てのサービスで提供されているわけではありませんので注意が必要です。
Only a few services implement a resource interface. They are defined by hand in JSON and have limitations. Please use the Client API instead. https://github.com/aws/aws-sdk-ruby#resource-interfaces
例えば、EC2では多くのメソッドが用意されていますが、
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/EC2/Resource.html
Step Functionsではメソッドがまだ用意されていませんでした。
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/States/Resource.html
どちらを使うかはユースケースに応じて使いやすい方を使えばよいと思います。
レスポンスのスタブ
作成したプログラムについて、RSpec等でテストを書くことがあると思います。APIを用いたプログラムのテストを書く場合、APIのレスポンスをどのように準備するかが問題となります。
AWS SDK for Rubyのレスポンスは属性値が多く、構造も少々複雑なため、出来れば簡単かつ正確にスタブを準備したいところです。そこで、AWS SDK for RubyではAPIレスポンスをスタブする仕組みが準備されていますので、その使い方を確認してみたいと思います。
例(クライアント方式)
クライアントのインスタンスを生成する際に stub_responses: true
を指定し、 stub_responses
メソッドで対応するメソッドと返り値の一部を指定すれば、以後そのインスタンスでそのメソッドが呼ばれた際は設定したダミーの値が返答されるようになります。
client = Aws::EC2::Client.new(stub_responses: true) client.stub_responses(:describe_volumes, { volumes: [ {volume_id: "volume-11111111", state: "available"}, ] }) response = client.describe_volumes pp response.volumes
出力結果は以下のようになります。ちゃんと本来返答される構造と属性値を持ったデータが得られています!
[#<struct Aws::EC2::Types::Volume attachments=[], availability_zone=nil, create_time=nil, encrypted=nil, kms_key_id=nil, outpost_arn=nil, size=nil, snapshot_id=nil, state="available", volume_id="volume-11111111", iops=nil, tags=[], volume_type=nil, fast_restored=nil, multi_attach_enabled=nil>]
例(リソースインターフェース方式)
リソースインターフェース方式の場合は、前述のようにクライアントのクラスを指定出来ますので、スタブを仕込んだクライアントのインスタンスを指定することで、レスポンスをスタブ可能です。
client = Aws::EC2::Client.new(stub_responses: true) client.stub_responses(:describe_volumes, { volumes: [ {volume_id: "volume-11111111", state: "available"}, ] }) ec2 = Aws::EC2::Resource.new(client: client) ec2.volumes.each do |volume| pp volume end
出力は以下のようになります。リソースインターフェース方式の構造に従って、レスポンスが得られていることが分かります。
#<Aws::EC2::Volume:xxxxxxx @client=#<Aws::EC2::Client>, @data= #<struct Aws::EC2::Types::Volume attachments=[], availability_zone=nil, create_time=nil, encrypted=nil, kms_key_id=nil, outpost_arn=nil, size=nil, snapshot_id=nil, state="available", volume_id="volume-11111111", iops=nil, tags=[], volume_type=nil, fast_restored=nil, multi_attach_enabled=nil>, @id="volume-11111111", @waiter_block_warned=false>
また、書き方としては以下のようにリソースのインスタンスを作成したあとでクライアントを呼び出してダミーのデータを仕込むことも可能です。
ec2 = Aws::EC2::Resource.new(stub_responses: true) ec2.client.stub_responses(:describe_volumes, { volumes: [ {volume_id: "volume-11111111", state: "available"}, ] })
おわりに
AWS SDK for Rubyのレスポンスのスタブの方法をまとめてみました。なにかの参考になれば幸いです。
お知らせ
サーバーワークスが提供しているAWS運用自動化ツール「Cloud Automator」のセミナーが11月26日にありますので、是非お申し込み頂けたらと思います。