動かざることバグの如し

近づきたいよ 君の理想に

AWSを使ったRspecテストにはモックを使うと簡単

環境

やりたいこと

Rubyを使ってS3とか扱うこと全然あると思う。例えば以下はバケット一覧を取得して表示するコード

require 'aws-sdk-s3'

client = Aws::S3::Client.new(
  region: 'ap-northeast-1'
)

bucket_data = client.list_buckets.buckets
bucket_data.each do |b|
  p b.name
end

が、S3ないしはAWSクラウドサービスなのでネットワークが入る。外部サービスをまたぐサービスを組み込んだコードのテストは結構厄介である。

これをなんとかしたい

方法1 非公式のモックライブラリを使う

例えばS3に限定した話だが、fake-s3というライブラリを使うとS3を振る舞うエンドポイントを作ることができる。

github.com

方法2localstack

もともとAtlassianが開発していたAWSのモックフレームワーク こっちは1と違ってフルスタックなので安心できる

github.com

懸念点としては今回みたいにピンポイントなモックを作るためだけにDockerを上げるべきなのかという点と引き継ぎ募集中!と書いてあって今後の開発が怪しいところ。。

方法3

公式が出しているモック機能を使う いわゆるstub

やり方

わりと簡単で手順は以下

  • 同じクラスをstub_responses: trueとして生成する
  • stub_responsesで振る舞いを定義

あとは実際のレスポンス同様にクラスが振る舞ってくれる。以下はさっきのをstubで書き換えたバージョン

require 'aws-sdk-s3'

client = Aws::S3::Client.new(stub_responses: true)
client.stub_responses(:list_buckets, buckets: [{name:'hoge'}, {name:'piyo'}])


bucket_data = client.list_buckets.buckets
bucket_data.each do |b|
  p b.name
end

しゅごい

取得にはエラーがつきもの 実はstub_responsesには例外も渡すことができる

require 'aws-sdk-s3'

client = Aws::S3::Client.new(stub_responses: true)
client.stub_responses(:list_buckets, Aws::Sigv4::Errors::MissingCredentialsError)

begin
  bucket_data = client.list_buckets.buckets
  bucket_data.each do |b|
    p b.name
  end
rescue => e
  puts "#{e.class} #{e.message}"
end

実行結果

Aws::Sigv4::Errors::MissingCredentialsError missing credentials, provide credentials with one of the following options:
  - :access_key_id and :secret_access_key
  - :credentials
  - :credentials_provider

当然オリジナルエラーも投げることができる

client = Aws::S3::Client.new(stub_responses: true)
client.stub_responses(:list_buckets, StandardError.new("custom error"))

これ他のSESとかAWSサービスでもstub使えるっていうだから設計すごいなぁというお気持ち