WebMock stub_request not working - ruby-on-rails

In my rails project, one of the initialisers requests and fetches certain data from S3.
S3.buckets[CONFIG['aws']['cdn_bucket']].objects['object_name'].read
This breaks the rspec test suite which uses webmock gem
WebMock.allow_net_connect!(:net_http_connect_on_start => true)
I get the following error when I try to run the test suite
WebMock::NetConnectNotAllowedError
You can stub this request with the following snippet:
stub_request(:get, "https://bucket.s3.amazonaws.com/object_name").with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'', 'Authorization'=>'AWS AKxxxxxx:Hyxxxxxxxxxx', 'Content-Type'=>'', 'Date'=>'Thu, 14 Apr 2016 15:10:18 GMT', 'User-Agent'=>'aws-sdk-ruby/1.60.2 ruby/1.8.7 i686-darwin15.3.0'}).to_return(:status => 200, :body => "", :headers => {})
Adding this stub does not fix the error. Infact, adding any of the following does not seem to make any change:
WebMock.stub_request(:any, /.*amazonaws.*/).with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'', 'Authorization'=>'AWS AKIxxxxxxxxxx:MSxxxxxxxx'}).to_return(:status => 200, :body => "stubbed response", :headers => {})
WebMock.stub_request(:any, /.*amazonaws.*/).to_return(:status => 200, :body => "stubbed response", :headers => {})
What is it that I am missing here? The detailed header in the error message does not seem to make sense here to allow all kinds of requests to S3
EDIT:
I just noticed that adding WebMock.disable! to the spec_helper also results in no change. Am I not adding the stub to the right place? Where should it be added if not in the spec_helper?

After sleeping over it, it was clear that the stub_request was being added to the wrong place. Adding it directly to the initialiser could have fixed this but that would have broken all other environments as the gem webmock is included for test env only.
Hence adding the following code snippet to the script fixed this
begin
require 'webmock'
WebMock.stub_request(:any, /testimonial/).to_return(:body => '')
rescue LoadError
end
S3.buckets[CONFIG['aws']['cdn_bucket']].objects['object_name'].read
This makes a stub_request if the gem is included else simply goes on as nothing happened.

Related

response.body is "" even though webmock .to_return(:body => "return") is set

I'm writing unit tests for a Rails service using minitest. A snippet of my test looks like this:
stub_request(:get, #service_url)
.with(:headers => #authheader)
.to_return(:body => "ABC", :status => 200)
get :index
After the get :index, response.code is 200 as expected (and I've changed it to 201 in the .to_return and seen that code be correct after get :index, but every time response.body is "". I'm sure I'm just doing something unimaginably dumb here and welcome being told what a dummy I am. :)
Thanks in advance
minitest 5.8.2
webmock 3.0.1
rails 4

In Rspec how does one download content

I want to download from a website during my rspec test
I do not want to stub the API usage for this specific function.
I don't plan on doing a heap of test against an API. For that I hope shall trust the stubbing.
But I think it's a good idea to do at least one test against an API.
Here is some sample code that replicates the issue I'm having:
require 'vcr'
context 'test vcr off' do
it 'should work' do
VCR.turn_off!
res = Net::HTTP.get_response(URI('http://www.google.com.au/?q=tester'))
print res.body
end
end
Here's the error I'm getting:
Failure/Error: res = Net::HTTP.get_response(URI('http://www.google.com.au/?q=tester'))
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: GET http://www.google.com.au/?q=tester with headers
{'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Host'=>'www.google.com.au', 'User-Agent'=>'Ruby'}
You can stub this request with the following snippet:
stub_request(:get, "http://www.google.com.au/?q=tester").
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Host'=>'www.google.com.au', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
The Doc's I've read on VCR indicate that the above code should work.
I really hate when this happens..
I search and search for an answer and then I post a question.
Then minutes later I find the answer..
1 require 'vcr'
2 context 'test vcr off' do
3 it 'should work' do
4 VCR.turn_off!
5 WebMock.disable_net_connect!(:allow => "www.google.com.au")
6 res = Net::HTTP.get_response(URI('http://www.google.com.au/?q=tester'))
7 print res.body
8 end
9 end
The answer was on line 5.. this let me make the call.. I mean I get a pile of junk on the screen, but it doesn't fail... no exception.

Test oauth2 get_token with rspec

I'm creating a controller spec for the get_token part of an oauth2 authentication. At this point the user has authorized my app and I need to generate and save the token and other information. Rspec fails with a somewhat cryptic error.
Failure/Error: get :auth, { code: "auth_code", scope: "read_write" }
OAuth2::Error:
{:token_type=>"bearer",
:stripe_publishable_key=>"PUBLISHABLE_KEY",
:scope=>"read_write",
:livemode=>"false",
:stripe_user_id=>"USER_ID",
:refresh_token=>"REFRESH_TOKEN",
:access_token=>"ACCESS_TOKEN"}
Here's the controller code. Rspec says it fails on get_token.
require 'oauth2'
def auth
code = params[:code]
client = oauth_client
token_response = client.auth_code.get_token(code, params: { scope: 'read_write' })
token = token_response.token
And here's the test. The webmock should be intercepting get_token. It is the autogenerated webmock suggeted by rspec that I filled in the body with the appropriate request and response body.
before do
stub_request(:post, "https://connect.stripe.com/oauth/token").
with(:body => {"client_id"=>"CLIENT_ID",
"client_secret"=>"SOME_SECRET",
"code"=>"auth_code",
"grant_type"=>"authorization_code",
"params"=>{"scope"=>"read_write"}},
:headers => {'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type'=>'application/x-www-form-urlencoded',
'User-Agent'=>'Faraday v0.9.0'}).
to_return(:status => 200,
:body => {token_type: "bearer",
stripe_publishable_key: "PUBLISHABLE_KEY",
scope: "read_write",
livemode: "false",
stripe_user_id: "USER_ID",
refresh_token: "REFRESH_TOKEN",
access_token: "ACCESS_TOKEN"},
:headers => {})
end
describe "#auth" do
it "creates a payment gateway" do
get :auth, { code: "auth_code", scope: "read_write"
end
end
This process already works in practice so at least the controller code is not to blame. What am I doing wrong?
After work hard having the same problem I have a solution. Is the header returned in the stub request, you have it blank. The oauth2 gem use the Content-Type to define how to parse the body. Try the stub putting the header in this way:
stub_request(:post, "https://connect.stripe.com/oauth/token").
with(:body => {"client_id"=>"CLIENT_ID",
"client_secret"=>"SOME_SECRET",
"code"=>"auth_code",
"grant_type"=>"authorization_code",
"params"=>{"scope"=>"read_write"}},
:headers => {'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Content-Type'=>'application/x-www-form-urlencoded',
'User-Agent'=>'Faraday v0.9.0'}).
to_return(:status => 200,
:body => {token_type: "bearer",
stripe_publishable_key: "PUBLISHABLE_KEY",
scope: "read_write",
livemode: "false",
stripe_user_id: "USER_ID",
refresh_token: "REFRESH_TOKEN",
access_token: "ACCESS_TOKEN"},
:headers => { 'Content-Type'=> 'application/json;charset=UTF-8'})
I think you're getting this error, because you've stubbed only part of oauth session, i.e. you're trying to send a stale token (or something like that) that has been provided by webmock.
Instead of manual stubbing of these requests, I'd suggest to use special tool: stripe-ruby-mock gem, which has been designed exactly for testing Stripe API.
As an alternative, you may use VCR gem (here are the docs), which allows to write on disk all your http session and play it as it was live. Great tool, highly recommended.

Why does my rspec test pass by itself but fails when I give a specific test to run?

When I run rspec to run all tests, they all pass, but when I give a specific filename, it fails.
$ rspec spec/controllers/refinery/books/admin/books_controller_spec.rb
2) Refinery::Books::Admin::BooksController GET :new responds 200 success
Failure/Error: login_admin_user #Gives 404
WebMock::NetConnectNotAllowedError:
Real HTTP connections are disabled. Unregistered request: POST http://localhost:8981/solr/default/update?wt=ruby with body '<?xml ...' with headers {'Content-Type'=>'text/xml'}
You can stub this request with the following snippet:
stub_request(:post, "http://localhost:8981/solr/default/update?wt=ruby").
with(:body => "<?xml ...",
:headers => {'Content-Type'=>'text/xml'}).
to_return(:status => 200, :body => "", :headers => {})
registered request stubs:
stub_request(:post, "http://localhost:8983/solr/test/update?wt=ruby").
with(:headers => {'Content-Type'=>'text/xml'})
============================================================
# ./lib/rsolr/connection.rb:15:in `execute'
# (eval):2:in `post'
# ./spec/support/controller_macros.rb:21:in `login_admin_user'
I have it mocked & stubbed already, so why is it failing? The only thing I can see is the existing stub is '/test/' and the request is '/default/'. How did that change?
Ok I ran into this problem again on another spec. Here is the spec:
https://gist.github.com/starrychloe/1d79d9925f9b79ae5c01
I did find this solr/solr.xml
<?xml version="1.0" encoding="UTF-8" ?>
<solr persistent="false">
<cores adminPath="/admin/cores" host="${host:}" hostPort="${jetty.port:}">
<core name="default" instanceDir="." dataDir="default/data"/>
<core name="development" instanceDir="." dataDir="development/data"/>
<core name="test" instanceDir="." dataDir="test/data"/>
</cores>
</solr>
It's like rspec is running in production environment, even if I explicitely give the environment
$ RAILS_ENV=test rspec spec/controllers/refinery/books/books_controller_spec.rb
I think it is an rspec problem. rspec -v: version 2.14.7.
I added stub_request(:post, "http://localhost:8981/solr/default/update?wt=ruby").to_return(:status => 200, :body => "", :headers => {}) before login_admin_user but I don't think that's the root solution.
It is probably interacting with another test.
Try running the suite in random order
rspec spec --order rand
and/or try running all the tests for that controller or or controllers.
You are using webmock, which disables all HTTP connections by default.
You can modify you Gemfile to disable auto require as followed:
gem "webmock", :require => false
And require webmock where you need it (e.g., require 'webmock/rspec'). After disabling HTTP connections for certain tests, remember to WebMock.allow_net_connect! to allow real http connections for other tests.
Another alternative is to WebMock.disable_net_connect!(:allow_localhost => true) to disable external requests while allowing localhost.

RSpec before suite not being run

I'm trying to stub any external API calls in my test suite, but the before(:suite) is never executed. Webmock always reports that I need to stub the maps.googleapis.com even though no tests have been run yet (no green dots, no red Fs).
spec_helper.rb:
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
...
config.before(:suite) do
puts "THIS NEVER SHOWS"
stub_request(:get, "maps.googleapis.com").
with(headers: {'Accept'=>'*/*', 'User-Agent'=>'Ruby'}).
to_return(status: 200, body: "", headers: {})
end
The geocoder gem ends up trying to save the lat/lon from googleapis.com and an error is raised by Webmock saying that the URL is unregistered.
EDIT: Error snippet:
$ bundle exec rspec spec/factories_spec.rb
/home/jake/.rvm/gems/ruby-2.1.0#global/gems/webmock-1.17.4/lib/webmock/http_lib_adapters/net_http.rb:114:in `request': Real HTTP connections are disabled. Unregistered request: GET http://maps.googleapis.com/maps/api/geocode/json?address=[private]&language=en&sensor=false with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} (WebMock::NetConnectNotAllowedError)
You can stub this request with the following snippet:
stub_request(:get, "http://maps.googleapis.com/maps/api/geocode/json?address=[private]&language=en&sensor=false").
with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
to_return(:status => 200, :body => "", :headers => {})
============================================================
from /home/jake/.rvm/gems/ruby-2.1.0#global/gems/geocoder-1.1.9...
...
Again, I'll stress that this has to do with the fact that the code in the config.before(:each) block is never run. Why? Because if it was, I could "raise 'WTF'" and 'WTF' should appear in the console output instead of the error you see above. I only see 'WTF' when I "un-bundle" the Webmock gem.
Well I was doing "something cute" with my RSpec tests by creating tests at runtime depending on whether or not the Factory has an attribute that is a file. Due to the way my factories/models were set up, factories were being created (saved) when the attributes for a certain factory were being read, so the block of code that's generating the tests runs outside of RSpec's config.before(:suite) and WebMock raises the error.
https://github.com/bblimke/webmock/issues/378
Moreover, here's specifically what I was doing wrong - not related to WebMock:
1) In my factories.rb, I was calling create() for associations which may not yet exist. Why? Because RSpec was giving me errors saying "[association] was blank". It was doing that because I had validates_presence_of :association_id instead of just :association. When I used create() instead of build(), it "worked". Of course when it came time to use WebMock, I was creating (and thus saving) objects calling geocoder to do it's thing. The solution was to fix validates_presence_of to use the right attribute and use build() instead of create() in my factories.
Bad Example:
# In spec/factories.rb
factory :review, class: Manager::Review do
rating 4
wine { Manager::Wine.first || create(:wine) }
reviewer { Manager::Reviewer.first || create(:reviewer) }
date Time.now
association :referral, referrable_id: 1, referrable_type: Manager::Review, strategy: :build
end
# In app/models/manager/review.rb
validates_presence_of :rating_id, :wine_id, :reviewer_id, :date
Good Example:
# In spec/factories.rb
factory :review, class: Manager::Review do
rating 4
wine { Manager::Wine.first || build(:wine) }
reviewer { Manager::Reviewer.first || build(:reviewer) }
date Time.now
association :referral, referrable_id: 1, referrable_type: Manager::Review, strategy: :build
end
# In app/models/manager/review.rb
validates_presence_of :rating, :wine, :reviewer, :date
2) FWIW, I told geocoder to fetch the geocode before_save, not after_validate like it suggests in their home page.
Also, you cannot stub with WebMock in the before(:suite), but it works in before(:each)

Resources