【基礎から学ぶ】AWS SDK for Rubyでレスポンスをスタブする

記事タイトルとURLをコピーする

サービス開発課のくればやしです。

はじめに

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日にありますので、是非お申し込み頂けたらと思います。

www.serverworks.co.jp

参考

github.com

aws.amazon.com

docs.aws.amazon.com

docs.aws.amazon.com

github.com