How do I mock AWS SDK (v2) with rspec?
I had a hard time finding examples mocking AWS resources. I spent a few days figuring it out and wanted to share my results on Stack Overflow for posterity. I used rspec-mocks (doubles & verifying doubles). Here's an example with the communicator.rb
example in the question.
communicator_spec.rb:
RSpec.describe Communicator do
describe "#consume_messages" do
it "can use rspec doubles & verifying doubles to mock AWS SDK calls" do
sqs_client = instance_double(Aws::SQS::Client)
allow(Aws::SQS::Client).to receive(:new).and_return(sqs_client)
SQSResponse = Struct.new(:messages)
SQSMessage = Struct.new(:body, :receipt_handle)
response = SQSResponse.new([SQSMessage.new(File.read('data/expected_body.json'), "receipt_handle")])
empty_response = SQSResponse.new([])
allow(sqs_client).to receive(:receive_message).
and_return(response, empty_response)
allow(sqs_client).to receive(:delete_message).and_return(nil)
Communicator.new.consume_messages
end
end
end
How to mock aws-sdk gem?
There are a lot of ways to mock requests in the AWS SDK for Ruby. Trevor Rowe recently posted an article on using the SDK's native support for object stubbing, which does not require any external dependencies like Webmock. You can also use tools like VCR (link will send you to another blog post) to build cacheable integration tests; this way you can test against the live service when you want accuracy and avoid hitting network when you want speed.
Regarding the get request on latest/meta-data/iam/security-credentials/
, this happens because the SDK is trying to look up credentials, and, if none are provided, it will check if you are running on an EC2 instance as a last resort, causing the SDK to make an extra HTTP request. You can avoid this check by simply providing bogus static credentials, though if you are using something like VCR, you will want to provide valid credentials for the first run. You can read about how to provide static credentials in another blog post that Trevor wrote on credential management (this should also be in the developer guide and SDK documentation).
How to mock aws-sdk gem?
There are a lot of ways to mock requests in the AWS SDK for Ruby. Trevor Rowe recently posted an article on using the SDK's native support for object stubbing, which does not require any external dependencies like Webmock. You can also use tools like VCR (link will send you to another blog post) to build cacheable integration tests; this way you can test against the live service when you want accuracy and avoid hitting network when you want speed.
Regarding the get request on latest/meta-data/iam/security-credentials/
, this happens because the SDK is trying to look up credentials, and, if none are provided, it will check if you are running on an EC2 instance as a last resort, causing the SDK to make an extra HTTP request. You can avoid this check by simply providing bogus static credentials, though if you are using something like VCR, you will want to provide valid credentials for the first run. You can read about how to provide static credentials in another blog post that Trevor wrote on credential management (this should also be in the developer guide and SDK documentation).
Stub or mock instance method rspec
Instead of stubbing Zlib::GzipReader
or the S3 Bucket
. One simple and easy way to handle such cases would be to create a new private function in the controller and then stub the controller function.
In the controller side:
def show
render json: s3_response
end
private:
def s3_response
Zlib::GzipReader.new(ApiBucket.bucket.object(id).get.body).read
end
The spec will be:
describe '#GET show' do
let!(:resource) { create(:resource) }
before do
json_data = Api::V2::Presenter.consume_as_json(resource)
ResourceController.any_instance.stub(:s3_response).and_return(json_data)
end
it 'should return the resource in page format' do
get :show, format: :json, params: { id: resource.uuid }
response_body_json = JSON.parse(response.body)
json_data = Api::V2::Presenter.consume_as_json(
Api::V2::Presenter.new(resource).page,
true
)
expect(response_body_json).to eql(JSON.parse(json_data))
end
end
Stub Calls to `Aws.config.update()`
Stubbing without specifying a return value has the effect you intend, for example:
expect(Aws.config).to receive(:update)
afer this, running Aws.config.update
will just return nil and not actually run anything. You could also use allow
.
You didn't specify what testing library you're using, but to be clear the example I gave is in RSpec.
Related Topics
Ruby - Array.Join Versus String Concatenation (Efficiency)
How to Run a Simple Ruby Script in Any Web Server (Apache or Mongrel or Any Thing Else)
Building Hash by Grouping Array of Objects Based on a Property of the Items
Raise Exception When Accessing Attributes That Doesn't Exist in Openstruct
How to Make the Url's in Ruby on Rails Seo Friendly Knowing a @Vendor.Name
Remove Rails Model After Migration
Adding a Helper Method with a Gem
Display Base64 Encoded Image in Rails
Ruby Net::Smtp - Send Email with Bcc: Recipients
Changing Value of Ruby Variables/References
Env: Ruby\R: No Such File or Directory
Rails Join a List of Strings with Commas and "And" Before the Last
Install Rvm "Bash /Root/.Rvm/Scripts/Rvm No Such File or Directory"