Rspec Suite Failing on OmniAuth Mock Auth Hash - ruby-on-rails

My Rspec tests are all passing individually but are failing as the whole suite.
I have narrowed the issue down to using the mock omniauth hash describe in spec/support/devise.rb:
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:facebook] = {
"uid" => "1111",
"provider" => "facebook",
"credentials" => {
"token" => "token",
"secret" => "secret"
},
"extra" => {
"raw_info" => {
"name" => "Adam Waite",
"username" => "adamjwaite",
"email" => "adam#adam.com"
}
}
}
OmniAuth.config.add_mock(:facebook, OmniAuth.config.mock_auth[:facebook])
When I inspect OmniAuth.config.mock_auth[:facebook] just before the tests fails (using pry) it returns :invalid_credentials if run in the suite. If I run the same test in an individual test file it appears as it's displayed.
Here's the failing test in the registration method in my UsersController:
describe "GET :new" do
describe "as an unauthenticated user with a facebook omniauth session" do
before do
session[:omniauth_facebook] = OmniAuth.config.mock_auth[:facebook]
get :new
end
specify { assigns[:registering_with_facebook].should == true }
specify { assigns[:registering_with_twitter].should == false }
specify { response.should be_success }
end
end
It's also worth mentioning that the application function correctly too. I would just like the suite to pass.
Anyone shine any light on what's happening?

Whenever a test works in isolation, but fails with other tests, you have a test ordering issue. Some earlier test is changing global state and leaving it that way, which negatively affects this test.
I've created a small tool to help me find ordering issues in my own suites: rspec-search-and-destroy. It will take your test suite and bisect it until it finds the one test that is setting the bad global state. Of course, you can do this yourself by hand, but hopefully the tool can automate the drudgery.
Once you have found the earlier test, then you need to inspect it to figure out what global state is being set and how you can properly sandbox that change to just the test that needs it.

Related

Rails 4 rspec UrlGenerationError

I am testing a Rails app that has been upgraded to Rails 4.2 using rspec 3.4.1. In this app I have the following route (as printed by rake routes):
contact_g GET /foo/contact/:legs_trip_id/:user_id(.:format)
foo#find_edit { :trip_type_id=>"123", :user_id=>"0", :locale=>"en", :format=>/html|js|json/, :legs_trip_id=>/\d+/}
In my app, in development and production modes, this route works fine. But in tests I get the following error when trying to use the route. Example spec:
describe "#find_edit, logged in:" do
before do
#c1 = mock_model(Foo, :id => 1059)
# mock a User, set up session and expectations to mimick a logged in user
#u = pseudo_login
end
it "..." do
expect(#u).to receive # ...
get :find_edit, :legs_trip_id => "123", :pn_id => 252, :sa_id => 1923, :email => 1, :ee_id => "bla", :trip_type_id => "123", user_id: #u.id
expect(response).to redirect_to(...)
end
As a controller test, at the "get" line rspec throws this error:
ActionController::UrlGenerationError:
No route matches { :action=>"find_edit", :controller=>"foo", :ee_id=>"bla", :email=>"1", :legs_trip_id=>"123", :pn_id=>"252", :sa_id=>"1923", :trip_type_id=>"123", :user_id=>"19" }
As a feature or integration specI can't access request.session to login my mocked user and thus can't get the tests to start at all:
undefined local variable or method `request' for #<RSpec::ExampleGroups::FindingByLTAndUserIDFindEditLoggedIn:...>
in this helper code that is called during spec setup:
request.session[:user_id] = mocked_user.id
How can I run these specs? It used to work in Rails 3.x.

rails wisper under test

I have a project which used wisper https://github.com/krisleech/wisper to provide publisher and subscribers functionalities.
The gem works perfectly under development and production modes. However, when I try to add some tests for them (rake test:integration), the newly added tests refused to work. The publisher (maybe also the listener) in the tests mode stopped working anymore.
Core::Request.subscribe(Listener::Studentlistener, async: true)
Core::Request.subscribe(Listener::Tutorlistener, async: true)
I used the sidekiq as a async backend, i used wisper-sidekiq gem to handle the async requests, not sure if this would be the problem?
,puma as the server, MRI ruby 2.0.0
Do I have to a set up something in order for the test to run?
it "Student can get latest status after looking for xxx tutor" do
post api_v1_students_request_look_for_xxx_tutor_path,
{ subject: 'nothing' },
{ "AUTHORIZATION" => "xxx"}
expect(response).to be_success
get api_v1_students_status_path, nil,
{ "AUTHORIZATION" => "xxx"}
expect(response).to be_success
json_response = JSON.parse(response.body)
expect(json_response['state']).to eq('matching')
end
The listener should receive the publishing between these two posts and update the state to be "matching". However, now when I run rspec the test failed because the publisher never publish anything and hence the state is not updated correctly.
Even the authors are relying on some mocking/stubbing in the integrations tests, so that might be the correct way.
class MyCommand
include Wisper::Publisher
def execute(be_successful)
if be_successful
broadcast('success', 'hello')
else
broadcast('failure', 'world')
end
end
end
describe Wisper do
it 'subscribes object to all published events' do
listener = double('listener')
expect(listener).to receive(:success).with('hello')
command = MyCommand.new
command.subscribe(listener)
command.execute(true)
end
https://github.com/krisleech/wisper/blob/master/spec/lib/integration_spec.rb

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)

Stubbing out Doorkeep Token using rspec_api_documentation

I'm building an API in Rails 4 using rspec_api_documentation and have been really impressed. Having opted to use DoorKeeper to secure my endpoints, I'm successfully able to test this all from the console, and got it working.
Where I am having difficulty now is how to spec it out, and stub the token.
DoorKeeper's documentation suggests using the following:
describe Api::V1::ProfilesController do
describe 'GET #index' do
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
it 'responds with 200' do
get :index, :format => :json
response.status.should eq(200)
end
end
end
However, I've written an acceptance test in line with rspec_api_documentation. This is the projects_spec.rb that I've written:
require 'spec_helper'
require 'rspec_api_documentation/dsl'
resource "Projects" do
header "Accept", "application/json"
header "Content-Type", "application/json"
let(:token) { stub :accessible? => true }
before do
controller.stub(:doorkeeper_token) { token }
end
get "/api/v1/group_runs" do
parameter :page, "Current page of projects"
example_request "Getting a list of projects" do
status.should == 200
end
end
end
When I run the test I get the following:
undefined local variable or method `controller' for #<RSpec::Core
I suspect this is because it's not explicitly a controller spec, but as I said, I'd rather stick to this rspec_api_documentation way of testing my API.
Surely someone has had to do this? Is there another way I could be stubbing the token?
Thanks in advance.
I had the same problem and I created manually the access token with a specified token. By doing that, I was then able to use my defined token in the Authorization header :
resource "Projects" do
let(:oauth_app) {
Doorkeeper::Application.create!(
name: "My Application",
redirect_uri: "urn:ietf:wg:oauth:2.0:oob"
)
}
let(:access_token) { Doorkeeper::AccessToken.create!(application: oauth_app) }
let(:authorization) { "Bearer #{access_token.token}" }
header 'Authorization', :authorization
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end
I wouldn't recommend stubbing out DoorKeeper in an rspec_api_documentation acceptance test. One of the benefits of RAD is seeing all of the headers in the examples that it generates. If you're stubbing out OAuth2, then people reading the documentation won't see any of the OAuth2 headers while they're trying to make a client.
I'm also not sure it's possible to do this nicely. RAD is very similar to a Capybara feature test and a quick search makes it seem difficult to do.
RAD has an OAuth2MacClient which you can possibly use, here.
require 'spec_helper'
resource "Projects" do
let(:client) { RspecApiDocumentation::OAuth2MACClient.new(self) }
get "/api/v1/group_runs" do
example_request "Getting a list of projects" do
status.should == 200
end
end
end

How to use Omniauth + capybara for testing FB Connect?

I have a rails 3 app + devise using capybara for integration tests. Right now I have sign and sign up tests working but don't have tests for FB Connect.
How can I add Omniauth tests to ensure sign up and sign in work? Any one have an example or a up to date tutorial that shows how this is done? All I could find is fragments of info.
Thanks
I don't have the complete example. I added the following to my test.rb(You can add it to a initializer and add it if the enviroment is test).
OmniAuth.config.test_mode = true
FACEBOOK_INFO = {
"id"=> "220439",
"email" => "bret#facebook.com",
}
OmniAuth.config.mock_auth[:facebook] = {
"uid" => '12345',
"provider" => 'facebook',
"user_info" => {"name" => "Bret Taylor", "nickname" => 'btaylor'},
"credentials" => {"token" => 'plataformatec'},
"extra" => {"user_hash" => FACEBOOK_INFO}
}
This simulates the call to omniauth. So in your test, when you simulate a click to the facebook button, the response you will get is the one from OmniAuth.config.mock_auth[:facebook].

Resources