问题
I'm trying to get my Cucumber test to work with Devise 1.5 and Omniauth 1.0, with Facebook authentication. Funny thing is, it works on development mode, but when run the Cukes test, it fails with this message:
undefined method `extra' for #<Hash:0x007f95f0d26260> (NoMethodError)
./app/models/user.rb:13:in `find_for_facebook_oauth'
./app/controllers/users/omniauth_callbacks_controller.rb:4:in `facebook'
(eval):2:in `click_link'
./features/step_definitions/web_steps.rb:58:in `/^(?:|I )follow "([^"]*)"$/'
features/facebook_auth.feature:11:in `When I follow "Sign in with Facebook"'
Here is the corresponding method:
def self.find_for_facebook_oauth(access_token, signed_in_resource=nil)
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
To get the Cukes test to be all green, I had to do this workaround, which then breaks the Development mode code. So for now, I'm doing this:
case Rails.env
when "test"
data = access_token['extra']['user_hash']
if user = User.find_by_email(data["email"])
user
else
User.create!(:email => data["email"], :password => Devise.friendly_token[0,20])
end
else
data = access_token.extra.raw_info
if user = User.where(:email => data.email).first
user
else
User.create!(:email => data.email, :password => Devise.friendly_token[0,20])
end
end
Seems like the offending line is data = access_token.extra.raw_info
.
The way I'm mocking the Facebook hash is:
OmniAuth.config.add_mock(:facebook, {
:uid => '12345',
:nickname => 'zapnap',
:extra => {
:user_hash => {
'email' => 'someone@webs.com'
}
}
})
And I have turned on OmniAuth.config.test_mode = true
by appending it at the last line of test.rb
.
Any ideas would be greatly appreciated!
回答1:
I got exactly the same error and could somehow get it to be all green. Although I use stub instead of mock as is written here, hope it can be a help.
As it is discussed here, this occurs because OmniAuth1.0 uses Hashie::Mash for the part we got error. So, we figured out to use a Hashie::Mash object to return instead of a Hash. In order to do so,
I added this to spec_helper..
require "omniauth"
and modified stub method like this..
def stub_env_for_omniauth
request.env["devise.mapping"] = Devise.mappings[:user]
pre = { "omniauth.auth" => { "provider" => "facebook", "uid" => "1234", "credentials" => {"token" => "abcdefg"}, "extra"=>{"raw_info" => {"id" => "1234567", "email" => "ghost@nobody.com", "name" => "Mark", "gender" => "male" }}}}
env = OmniAuth::AuthHash.new(pre)
@controller.stub!(:env).and_return(env)
end
This works for me.
回答2:
You can place settings in the spec_helper.rb, as alternative to stub_env_for_omniauth method.
OmniAuth.config.test_mode = true OmniAuth.config.full_host = 'http://example.com' omni_hash = { 'uid' => '12345', 'provider' => 'facebook', "info" => { "email" => Faker::Internet.email, "image" => "http://example.com/pic.img" }, "extra" => { "raw_info" => { "first_name" => Faker::Name.first_name, "last_name" => Faker::Name.last_name, "name" => Faker::Internet.user_name, "gender" => "male", "locale" => "en" } } } OmniAuth.config.mock_auth[:facebook] = OmniAuth::AuthHash.new(omni_hash)
回答3:
For Cucumber tests an additional mock object seems to work:
Given /^a new facebook user "([^"]*)"$/ do |name|
OmniAuth.config.test_mode = true
OmniAuth.config.mock_auth[:facebook] = {
:provider => 'facebook',
:uid => '123545',
:name => name}
m = mock("mock_extra",
:raw_info =>
mock("mock_raw_info",
:email => 'someone@webs.com',
:first_name => name,
:last_name => "xyz"))
OmniAuth.config.mock_auth[:facebook].stub(:extra).and_return(m)
end
来源:https://stackoverflow.com/questions/8556439/devise-1-5-omniauth-1-0-facebook-undefined-method-extra