Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
14
Help us understand the problem. What is going on with this article?
@Y_uuu

AWS SDK for Rubyのクライアントをスタブ化する

More than 3 years have passed since last update.

AWS SDKを使用する機能をテストする際、そのままテストすると「都度AWSへのアクセスが発生してしまう」ということを熟慮しなければなりません。テストの内容・頻度によっては思わぬ額の料金請求が発生したり、AWS側からペナルティを受けたりする懸念があります。

本記事では、上記問題の回避策としてAWS SDKを使用する機能をテストする際にクライアントをスタブ化する方法をまとめます。

例題

以下のように、AWSへアクセスする関数start_instancesをテストするケースを想定します。

aws_sample.rb
require 'aws-sdk'
require 'minitest/autorun'

# テスト対象の関数
def start_instances
  # EC2のクライアントを生成する
  credential = Aws::AssumeRoleCredentials.new(region: REGION, client: Aws::STS::Client.new,
                                              role_arn: ROLE_ARN, role_session_name: 'sample_client')
  client = Aws::EC2::Client.new({credentials: credential})

  # EC2のインスタンスを起動する
  resp = client.start_instances(instance_ids: [EC2_INSTANCE_ID])
end

# テストコード
class Ec2StartTest < Minitest::Test
  def test_ec2_start
    resp = start_instances
    assert resp != nil
  end
end

このまま実行してもテストはPASSします。
ただし、テストを実行するごとにclient.start_instancesによるAWSとの通信が発生します。
AWSとの通信を発生させずに、テストする方法を考えてみましょう。

解決方法

1. Aws.configを使用する

事前にAws.config[:stub_responses] = trueを実行することで、 Aws::EC2::Client.newで生成されるクライアントをスタブ(AWSとは通信をせずに結果を応答するオブジェクト)とすることができます。

「コードを1行追加するだけ」とお手軽な方法である反面、AWS SDKによって生成される全クライアントがスタブとなるため、特定のクライアントのみをスタブ化することはできない点に注意が必要です。

aws_sample.rb
require 'aws-sdk'
require 'minitest/autorun'

if Rails.env.test?
  Aws.config[:stub_responses] = true # !!!追加
end

# テスト対象の関数
def start_instances
  # EC2のクライアントを生成する
  credential = Aws::AssumeRoleCredentials.new(region: REGION, client: Aws::STS::Client.new,
                                              role_arn: ROLE_ARN, role_session_name: 'sample_client')
  client = Aws::EC2::Client.new({credentials: credential})

  # EC2のインスタンスを起動する
  resp = client.start_instances(instance_ids: [EC2_INSTANCE_ID])
end

# テストコード
class Ec2StartTest < Minitest::Test
  def test_ec2_start
    resp = start_instances
    assert resp != nil
  end
end

2. Client生成時のオプションを変更する

クライアント生成時の引数を変更することで、生成されるクライアントをスタブ化することができます。

aws_sample.rb
require 'aws-sdk'
require 'minitest/autorun'

# テスト対象の関数
def start_instances
  # EC2のクライアントを生成する
  credential = Aws::AssumeRoleCredentials.new(region: REGION, client: Aws::STS::Client.new,
                                              role_arn: ROLE_ARN, role_session_name: 'sample_client')

  if Rails.env.test?
    client = Aws::EC2::Client.new(stub_responses: true) # !!!追加
  else
    client = Aws::EC2::Client.new({credentials: credential})
  end

  # EC2のインスタンスを起動する
  resp = client.start_instances(instance_ids: [EC2_INSTANCE_ID])
end

# テストコード
class Ec2StartTest < Minitest::Test
  def test_ec2_start
    resp = start_instances
    assert resp != nil
  end
end

スタブの戻り値を設定する

上記方法で作成したスタブは、メソッド呼び出し時に何かしらの戻り値を返すものの、その内容はいわゆる空の応答です。このため、以下のように応答の内容を参照する場合において意図しない動作となります。

aws_sample.rb
require 'aws-sdk'
require 'minitest/autorun'

if Rails.env.test?
  Aws.config[:stub_responses] = true
end

# テスト対象の関数
def start_instances
  # EC2のクライアントを生成する
  credential = Aws::AssumeRoleCredentials.new(region: REGION,
                                              client: Aws::STS::Client.new,
                                              role_arn: ROLE_ARN,
                                              role_session_name: 'sample_client')

  client = Aws::EC2::Client.new({credentials: credential})

  # EC2のインスタンスを起動する
  resp = client.start_instances(instance_ids: [EC2_INSTANCE_ID])
end

# テストコード
class Ec2StartTest < Minitest::Test
  def test_ec2_start
    resp = start_instances
    assert resp != nil
    assert_equal 'i-xxxx', resp.starting_instances[0].instance_id # !!!応答を参照する処理
  end
end

有効な応答を返してもらう時はclient.stub_responsesを事前に呼び出し、第1引数に呼び出すメソッドのシンボルを、第2引数に返して欲しい応答の内容を設定します。

aws_sample.rb
require 'aws-sdk'
require 'minitest/autorun'

if Rails.env.test?
  Aws.config[:stub_responses] = true
end

# テスト対象の関数
def start_instances
  # EC2のクライアントを生成する
  credential = Aws::AssumeRoleCredentials.new(region: REGION,
                                              client: Aws::STS::Client.new,
                                              role_arn: ROLE_ARN,
                                              role_session_name: 'sample_client')

  client = Aws::EC2::Client.new({credentials: credential})

  if Rails.env.test?
    # スタブに応答を設定する
    client.stub_responses(:start_instances, { starting_instances: [ { instance_id: 'i-xxxx' } ] }) # !!!追加
  end

  # EC2のインスタンスを起動する
  resp = client.start_instances(instance_ids: [EC2_INSTANCE_ID])
end

# テストコード
class Ec2StartTest < Minitest::Test
  def test_ec2_start
    resp = start_instances
    assert resp != nil
    assert_equal 'i-xxxx', resp.starting_instances[0].instance_id # !!!応答を参照する処理
  end
end

注意

上記方法はクライアントをスタブ化する方法です。
モックではない(≒クライアントに対するメソッド呼び出し回数や引数の検証は実施しない)点に注意しましょう。

まとめ

AWS SDK for Rubyのクライアントは上記の通り容易にスタブ化することができます。
スタブを上手く活用して、テスト時間短縮、不要なアクセス回避を心がけるようにしましょう。

14
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Y_uuu
fusic
個性をかき集めて、驚きの角度から世の中をアップデートしつづける。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
14
Help us understand the problem. What is going on with this article?