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:)
JSON.parse File.read(filename)
end
end
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"
sent_emails.push(JSON.parse(response))
end
sent_emails
end
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}"\
"@api.mailgun.net/v3/#{configuration.api_domain}/events",
: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))
delivered_to_target.push(delivered)
end
end
delivered_to_target
end
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
end
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"],
TypeError:
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 RestClient::Request.execute
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.
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.