I'm looking at the testing docs for Savon here and i don't understand what's going on. I'm fairly new to testing with mocks and stubbing and maybe that's the issue. Here is the example:
require "spec_helper"
# require the helper module
require "savon/mock/spec_helper"
describe AuthenticationService do
# include the helper module
include Savon::SpecHelper
# set Savon in and out of mock mode
before(:all) { savon.mock! }
after(:all) { savon.unmock! }
describe "#authenticate" do
it "authenticates the user with the service" do
message = { username: "luke", password: "secret" }
fixture = File.read("spec/fixtures/authentication_service/authenticate.xml")
# set up an expectation
savon.expects(:authenticate).with(message: message).returns(fixture)
# call the service
service = AuthenticationService.new
response = service.authenticate(message)
expect(response).to be_successful
end
end
end
I understand that we set up an expectation with the fixture i.e. what the response should be.
We then call the service and get a response. My questions are:
1. Is a real call being made?
2. Is this response a real response??
3. Can someone try to explain this overall for me please?
Cheers
No remote request would be made. Since you have mocked authenticate, the response will be short-circuited to your designated value. However, some other preliminary requests might be expected to happen first, like a GET for the WSDL.
Related
When using the mandril-api the Rails Action Mailer is bypassed, so it is not possible to do something like this
it 'sends an email' do
expect { subject.send_instructions }
.to change { ActionMailer::Base.deliveries.count }.by(1)
end
I am trying to use an object_double to test my mailers. What I am trying to test is exactly what parameters are sent to the API (via an options hash).
So far, I have the Mandrill code here
MANDRILL.messages.send_template( options[:template], [], message) unless Rails.env.staging?
Where MANDRILL is just the connection to the API, as detailed below.
describe 'verify content' do
it 'uses the correct template' do
api = object_double(Mandrill::API.new(ENV['MANDRILL_KEY']).messages)
allow(api).to receive(:send_template)
PostNotificationMailer.format_options(participant, post)
expect(api).to have_received(:send_template)
#expect(options[:template]).to eq('crowdai_standard_template')
end
end
I'd like to be able to check all the params passed to the Mandrill API here. I can mock the messages method but not the messages.send_template
1) PostNotificationMailer verify content uses the correct template
Failure/Error: expect(api).to have_received(:send_template)
(ObjectDouble(#<Mandrill::Messages:0x007f8debd4f348 #master=#<Mandrill::API:0x007f8debd4faf0 #host="https://mandrillapp.com", #path="/api/1.0/", #session=#<Excon::Connection:7f8debd4f758 #data={:chunk_size=>1048576, :ciphers=>"HIGH:!SSLv2:!aNULL:!eNULL:!3DES", :connect_timeout=>60, :debug_request=>false, :debug_response=>false, :headers=>{"User-Agent"=>"excon/0.51.0"}, :idempotent=>false, :instrumentor_name=>"excon", :middlewares=>[Excon::Middleware::Hijack, Excon::Middleware::ResponseParser, Excon::Middleware::Expects, Excon::Middleware::Idempotent, Excon::Middleware::Instrumentor, Excon::Middleware::Mock], :mock=>false, :nonblock=>true, :omit_default_port=>false, :persistent=>false, :read_timeout=>60, :retry_limit=>4, :ssl_verify_peer=>true, :ssl_uri_schemes=>["https"], :stubs=>:global, :tcp_nodelay=>false, :thread_safe_sockets=>true, :uri_parser=>URI, :versions=>"excon/0.51.0 (x86_64-darwin15) ruby/2.3.1", :write_timeout=>60, :host=>"mandrillapp.com", :hostname=>"mandrillapp.com", :path=>"", :port=>443, :query=>nil, :scheme=>"https"} #socket_key="https://mandrillapp.com:443">, #debug=false, #apikey="redacted">>) (anonymous)).send_template(*(any args))
expected: 1 time with any arguments
received: 0 times with any arguments
# ./spec/mailers/post_notification_mailer_spec.rb:14:in `block (3 levels) in <top (required)>'
** EDIT **
There is a gem MandrillMailer which solves the problem of testing against the Mandril API, but it's build is broken and it also seems to rebuild the API internally.
How do I test mandrill api with rspec
I couldn't find any tutorials or clear examples on how to use object_double.
Have you thought about using the VCR gem ( https://github.com/vcr/vcr ) to record the response from the API call to mandrill into a fixture? Once the request is recorded, you can assert the values on the response to verify the expected data was passed.
As best as I can tell from your code, PostNotificationMailer.format_options(participant, post) has no way to know that its code is supposed to be sending the send_template method to your double instead of the predefined MANDRILL.messages object. If you call Mandrill::API.new(ENV['MANDRILL_KEY']) in your test, that returns a completely different object from MANDRILL even if you defined MANDRILL with the exact same code. So when the mailer sends the method to MANDRILL.messages, your double is oblivious.
Unfortunately, even if your test was rewritten to make the double based on MANDRILL.messages, it still wouldn't be the same object as what's in your mailer, because the mailer is calling the real MANDRILL.messages and not your double. The way I understand it, for most doubles you still have to use dependency injection. That is, your mailer would have to be set up so that you could set a parameter that would be "the object that does the mailing," something like (I'm making this up) PostNotificationMailer.set_api(some_object). In production, it would be PostNotificationMailer.set_api(MANDRILL), while in your test it would be PostNotificationMailer.set_api(api). Possibly that's more trouble than it's worth.
This seems to be confirmed by the object_double documentation, where the test includes:
user = object_double(User.new, :save => true)
expect(save_user(user)).to eq("saved!")
As you can see, the user object is passed as a parameter into the method that we're trying to test so that methods are called on the double and not some other object.
RSpec does seem to have the interesting ability to use object doubles on constants, so that you don't have to use dependency injection. However, based on the relevant documentation, it looks like you have to pass the object name as a string (not the actual object reference) and then you have to call as_stubbed_const on the double:
logger = object_double("MyApp::LOGGER", :info => nil).as_stubbed_const
Email.send_to('hello#foo.com')
expect(logger).to have_received(:info).with("Sent to hello#foo.com")
So maybe if your application defined a constant for the API's messages object, and then passed in its name as a string and called as_stubbed_const, it would work. I haven't tried using RSpec's doubles like this, so I can't say for sure.
I have AJAX calls initiated by Rails UJS that I would like to test. specifically, I have used Rails UJS ajax events to provide for cases of errors.
I would like to test them but I don't know how to tell rspec/capybara to "stub" and assume the error code
$("button").
on('ajax:error',function(event,xhr, status, error){
if(status == "timeout") {
var msg;
msg = Messenger().post({
message: "This is taking too long"
});
} else {
var msg;
msg = Messenger().post({
message: "it seems there is a bug. Please try again."
});
};
});
I would like to do something like the following:
describe "test returning error message", js: true do
it "should be successful" do
visit deal_page_path(deal)
first('a.button').click
stub(:xhr.status) = "timeout"
expect(page).to have_content('This is taking too long')
end
end
How to do this?
Note: the ajax requests are internal they don't go to third party API or services (such as facebook for ex).
When testing with Capybara (JS enabled drivers) it has no access to the request or response except through the changes it creates in the browser. You could build a test mode into your relevant controllers that could be turned on and off to allow it to output the errors you want, but the cleanest way to do this is probably to use a programmable proxy like puffing-billy which will allow you to selectively return whatever you'd like for any given request from the browser. One thing to realize is that this isn't testing that app correctly returns errors, it's just testing that your front-end handles errors the way you expect.
I'm using rspec to test my application and I'm having a hard time figuring out how to test this. The Slack::Notifier's job is to send a post request to a webhook. Once I call this method in Rspec, I don't know how to see the response. Also, is it possible to match the format of this text to an expected text somewhere? My method is below. Thanks.
def notify
offset = 14400 #UTC to EST
notifier = Slack::Notifier.new Rails.application.secrets.slack_organization_name, Rails.application.secrets.slack_token, channel: "##{Rails.application.secrets.slack_channel}", username: Rails.application.secrets.slack_user_name
notifier.ping(":white_check_mark: *USAGE SUMMARY for #{(Time.now - offset).to_formatted_s(:long) }*")
count = 0
current_time = Time.now.to_i
live_response.each do |r|
if r["properties"]["time"] > ((current_time - offset) - 60) #&& r["properties"]["$initial_referring_domain"] == "capture.com"
notifier.ping("
*Name:* #{r["properties"]["$name"]}
*Event:* #{r["event"]}
*Keywords:* #{r["properties"]["keywords"]}
*Organization:* #{r["properties"]["organizationName"]}
*Email:* #{r["properties"]["$email"]}
*Time:* #{Time.at(r["properties"]["time"] + offset).utc.to_datetime.in_time_zone("Eastern Time (US & Canada)").to_formatted_s(:long_ordinal)}
*More Data:* #{ANALYTICS_URL}#{r["properties"]["distinct_id"]}
__________________________________________________
")
count +=1
end
end
notifier.ping("*There were #{count} events in this report.*")
end
Testing network communications (like API calls) is a tricky thing. Personally I would rely on programming by contract and testing in isolation - i.e. assume the external service is working fine and it responds positively for valid request.
Then you test your client code by checking that you are actually sending a valid request. For this stub the method where control exits your code into a library/system code. For example if you are making a HTTP GET request using a gem like HTTParty, then stub HTTParty.get i.e. HTTParty.stub(:get) and in that stub verify that correct parameters were sent.
On the other side of the spectrum you should also simulated both positive and negative responses from the web service and make sure your client code handles it in expected manner.
If you are making a real then you are introducing a lot of dependencies on your test : a test setup of external service, risk of network issues (timeout, n/w breakdown, etc) problems with external service and may be more.
If you yourself are writing that webservice too then test that one also in isolation, i.e by simulating valid and invalid inputs and making sure they are handled properly. This part is pretty much your controller specs or request specs.
Once again, this is my opinion. Suggestions to do this in a better way and constructive criticism on the shortcomings of this approach are definitely welcome.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
I have been writing my Rails application with Cucumber in TDD mode: Tests first, then the code. Now my application needs an API. What I like about cucumber is, that I can specify my tests in plain English, so even managers understand what's going on.
Is there any way I can do this for my JSON-API?
YES! This is totally possible. Have you checked out the Cucumber Book by the Pragmatic Programmer series?
Here's a quick example:
Feature: Addresses
In order to complete the information on the place
I need an address
Scenario: Addresses
Given the system knows about the following addresses:
[INSERT TABLE HERE or GRAB FROM DATABASE]
When client requests GET /addresses
Then the response should be JSON:
"""
[
{"venue": "foo", "address": "bar"},
{ more stuff }
]
"""
STEP DEFINITION:
Given(/^the system knows about the following addresses:$/) do |addresses|
# table is a Cucumber::Ast::Table
File.open('addresses.json', 'w') do |io|
io.write(addresses.hashes.to_json)
end
end
When(/^client requests GET (.*)$/) do |path|
#last_response = HTTParty.get('local host url goes here' + path)
end
Then /^the response should be JSON:$/ do |json|
JSON.parse(#last_response.body).should == JSON.parse(json)
end
ENV File:
require File.join(File.dirname(__FILE__), '..', '..', 'address_app')
require 'rack/test'
require 'json'
require 'sinatra'
require 'cucumber'
require 'httparty'
require 'childprocess'
require 'timeout'
server = ChildProcess.build("rackup", "--port", "9000")
server.start
Timeout.timeout(3) do
loop do
begin
HTTParty.get('local host here')
break
rescue Errno::ECONNREFUSED => try_again
sleep 0.1
end
end
end
at_exit do
server.stop
end
You can definitely achieve this. You can write step definitions to assert/verify your json responses. Something like this
Given a username and password
When I try to login via the API
Then I should get logged in
While this works, this just tests the API ( controllers/actions ) work or not, ie more like "functional" testing, not Acceptance testing. As such you are not going to test the API consumer itself.
hi I'm create test use cucumber in my rails apps. in my step scenario I used http basic authenticate, so far it pass the basic authenticate, but when I wanna to call method on controller and post some params, I had problem :
first I use this code in step but failed not cross to method on controller :
post some_admin_url, #params
second I used this code, and failed also, the error is when running the URI.parse redirect to "www.example.com" I want to go "localhost:3000/admin", so I can match the data :
Net::HTTP.post_form(URI.parse(some_admin_url), {'from' => '2005-01-01','to' => '2005-03-31'}) { |i| }
FUTURE :
#selenium
Scenario: Admin want to activate user
Given one user logged in as admin
And admin page
STEPS :
Given /^one user logged in as admin$/ do
create_user_admin
visit '/user_sessions/new'
fill_in 'user_session[login]', :with=>'siadmin'
fill_in 'user_session[password]', :with=>'12345'
click_button 'Anmelden'
end
Given /^admin page$/ do
require "net/http"
require "uri"
uri = URI.parse(user_action_admin_users_url)<br/>
http = Net::HTTP.new(uri.host, uri.port)<br/>
request = Net::HTTP::Get.new(uri.request_uri)<br/>
request.basic_auth("username", "password")<br/>
response = http.request(request)<br/>
end
enter code here
HELP !!!
THANKS
fl00r's answer probably works, but wouldn't catch redirects.
If you want to catch redirects you have to change this in a higher level. For some test frameworks there is a way to set the default host.
Using Capybara we did that:
Capybara.default_host = "subdomain.yourapp.local"
Now we actually use a Rack middleware (in test env only :), that changes changes the env[HTTP_HOST] transparently for the application, so we don't have to care about which testframework / browser driver we use.
What framework do you use?
uname="myapp"
Capybara.default_host = "http://#{uname}.mywebsite.com:3000"
Capybara.server_port = 3000 # could be any of your choice
Capybara.app_host = "http://#{uname}.mywebsite.com:#{Capybara.server_port}"
OR
request.host="www.myapp.com"
,when you are extending ActionController::TestCase
try to change url to path
post some_admin_path, #params
or
post "http://localhost:3000/#{some_admin_url}", #params