Gia Phat Ha Gia Phat Ha - 1 year ago 136
Ruby Question

ruby rspec mocks test function receiving a hash

I am toying with the mailgun API, all functionalities seem to work now and I want to test them using rspec mocks.

I made some json fixtures under the rspec/fixtures folder, each has a json representing the expected result when I call a particular function. I also made a small helper:

module TestHelpers
def self.get_json(filename:)

What I want to test is this function:

def self.get_messages_for(email:)
sent_emails = []
delivered_events = get_events_for(email: email)

# :Accept => "message/rfc2822" will help us to get the raw MIME
delivered_events.each do |event|
response = RestClient::Request.execute method: :get ,
url: event["storage"]["url"],
user: "api", password:"#{configuration.api_key}",
Accept: "message/rfc2822"

Which uses this helper to fetch events:

def self.get_events_for(email:, event_type: "delivered")
delivered_to_target = []
response = RestClient.get "https://api:#{configuration.api_key}"\
:params => {
:"event" => event_type

all_delivered = JSON.parse(response)["items"]

all_delivered.each do |delivered|
if (delivered.has_key?("recipients") and delivered["recipients"].include?(email)) or
(delivered.has_key?("recipient") and delivered["recipient"].include?(email))

And here in my spec I have:

it 'can get the list of previously sent emails to an email address' do

allow(StudySoup).to receive(:get_events_for).with({email: email}) {
Array(TestHelpers::get_json(filename: 'spec/fixtures/events.json'))

allow(RestClient::Request).to receive(:execute).with(any_args){
TestHelpers::get_json(filename: 'spec/fixtures/messages.json')

expect(StudySoup.get_messages_for(email: email)["subject"]).not_to be nil

However, when I tried to run rspec, it always has the following fail trace:

1) StudySoup can get the list of previously sent emails to an email address
Failure/Error: url: event["storage"]["url"],

no implicit conversion of String into Integer
# ./lib/StudySoup.rb:51:in `[]'
# ./lib/StudySoup.rb:51:in `block in get_messages_for'
# ./lib/StudySoup.rb:49:in `each'
# ./lib/StudySoup.rb:49:in `get_messages_for'
# ./spec/StudySoup_spec.rb:86:in `block (2 levels) in <top (required)>'

I thought I stubbed out the
method so it should work but it didn't. Any ideas on how I can correctly test this function? I tried to put many params matcher like anything(), hash_including(:key => value)... but it still didn't work.

Answer Source

You have indeed stubbed out the execute method. This means that rspec fiddles with that class so that attempts to call execute call rspec provided code instead of the original inplementation. In particular, all the arguments to that call are evaluated as normal.

Changing the argument matchers as you have tried changes whether rspec decides the method call matches one of the configured stubs, but can't avoid the fact that evaluating event["storage"]["url"] is raising an exception.

When you stubbed get_events_for you returned an array of arrays instead of an array of hashes: Array calls to_ary or to_a on its arguments which is turning your hash into an array of key value pairs, rather than wrapping the hash in an array as I think you thought it did.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download