Integration testing of spree (alongside refinery) - ruby-on-rails

I have just started a new rails app. So far there are no controllers or models there is simply the two engines: refinery and spree working next to each other.
They are mounted as following:
Store::Application.routes.draw do
mount Spree::Core::Engine, :at => '/shop'
mount Refinery::Core::Engine, :at => '/'
end
Now i have set up a few pages using refinery so when I go to / then i see the refinery home page and can click the about us page etc. When i go to /shop then i see the spree section of the site which is also working well.
Now i would like to write a small test that the spree engine is correctly mounted at '/shop'. I tried with the following test:
require 'test_helper'
class SpreeEngineTest < ActionDispatch::IntegrationTest
test "has been correctly mounted" do
get "/shop"
assert_response :success
end
end
But it fails with the result:
Expected response to be a <:success>, but was <302>
I looked into the body of the request and it contains the following:
"<html><body>You are being redirected.</body></html>"
I am using the standard testunit package and rails 3.2
Thanks for your help!

Maybe you should use assert_redirected_to instead of assert_response.
http://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html

Sorry for the late answer. The reason is that Refinery will redirect you to the create user dialog when it detects that no users with the correct roles exists in the database. So my guess is that you're running this test without any users registered in the test database, and that causes the redirect.
In your integration test, add the following setup method:
def setup
user = Refinery::User.new(:username => 'testuser', :email => 'test#example.com', :password => 'test', :password_confirmation => 'test')
assert user.create_first
end
This will ensure that a default user with the right roles exist and the rest of the test should be ok.

Related

Mount additional routes in Rails specs

I want to test 3rd party sites embedding a javascript widget (provided by my Rails app) and submitting forms to an endpoint in my rails app.
To do this, I have created a dummy sinatra app that I test with Capybara:
# spec/system/widget_spec.rb
require 'sinatra/base'
class DummyApp < Sinatra::Base
get '/form' do
#widget_code = params[:widget_code]
erb :form
end
end
require 'rails_helper'
...
I'm defining this app before Rails is loaded in my spec, so that I can do the following in routes.rb:
mount DummyApp => '/dummy' if defined? DummyApp
This works if I run just this one spec, but if I run the whole suite, Rails is loaded before my DummyApp is defined.
Ideally I'd like to pull this line out of routes.rb entirely and allow my test to inject custom routes, so my routes file isn't cluttered with test-related routes.
1) Is there a way to tinker with rails routes within tests?
2) If not, what's the best way to go about testing this type of scenario?
There is a helper with_routing you could use (docs):
with_routing do |set|
set.draw do
mount DummyApp => '/dummy'
end
# ...
end

separate helper function to log in before every rspec request test is run

I've been struggling with creating a login function that should be executed before any rspec test is run.
What I have right now is:
def login
post "/session", { "session[username]" => "bjones" }
end
in my spec_helper.rb file
Then, I have the following in one of my spec.rb files in the requests directory.
require 'spec_helper'
describe "Sessions" do
describe "GET /dashboards" do
login
it "displays the dashboard" do
get dashboard_path
puts response.body
end
end
end
However, when I try running the test, I get:
undefined method `post' for #<Class:0x4f08828> (NoMethodError)
I'm pretty new to rails and completely new to testing and rspec so maybe there's something fundamental I'm missing here. Basically, all I want to do is set that session variable so that when the test is run I will be logged in. Perhaps a different approach would be better? Or maybe I need to put that login function in a different place?
I came across this answer which was sort of useful but it's not for rspec so I'm having trouble understanding where such a helper function would go.
Try
let(:login) {
post "/session", { "username" => "bjones" }.to_json
}
This might have to be revised to use .to_json or not, depending on what content type the controller accepts.

Skip Rails http_basic_authenticate_with in RSpec test

I'm working on a MVP (minimum viable product). In order to offer a simpler way to secure the admin pages, I just did add http_basic_authenticate_with to my AdminController.
Problem is that when I want to test my AdminController, I get "unauthorized" (401) for not being logged in.
In this scenario, it's irrelevant to test the authentication - it's just temporary, and as soon I go to the next sprint, it's going to removed -, so I'm trying to skip it within RSpec.
Problem is I tried many ways, and none seems to be working.
For example, I tried to modify the http_basic_authenticate_with in order to avoid the authentication. Like this:
require 'spec_helper'
module ActionController
module HttpAuthentication
module Basic
def http_basic_authenticate_with(*args)
end
end
end
end
describe Admin::SubscribersController do
describe "GET 'index'" do
it "should be OK" do
get 'index'
response.should be_successful
end
end
end
But when I run it, it still returns "false" for that simple test.
Btw, in order to simplify this test, I just have an empty index action on my AdminController and an empty view (index.html.erb).
Finally I got it working.
Something as stated in the docs didn't work for me:
get 'index', nil, 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials("admin", "password")
So I tried an "old" approach to do it, which is to set request.env['HTTP_AUTHORIZATION'] :
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials("admin","password")
None of the other solutions worked, so I will just keep in the meanwhile with this one.
Thanks.
If it is ok to skip authentication for all tests for a controller, here's the technique I'm using on a current project.
unless Rails.env.test?
http_basic_authenticate_with name: "slothbear", password: "kuniklo"
end
In Rails 5.x, this works:
allow(subject).to receive(:authenticate_or_request_with_http_basic)
.with(anything).and_return true
In Rails 6.x, this works:
allow(subject).to receive(:http_basic_authenticate_or_request_with)
.with(anything).and_return true
This is because http_basic_authenticate_with is a class-level method that adds a before_action that actually calls one of these two methods under the hood.
You can see which one to use by checking out http_authentication.rb here for Rails 6 or here for Rails 5
You can or even should test authentication. Write test for unauthenticated (it is now) and authenticated.
See Testing HTTP Basic Auth in Rails 2.2+ it should help.

Writing functional tests for facebooker controller?

Anyone have any tips for best practices for mocking out facebook requests in functional tests? Is it just as simple as adding all of the proper params to the request? Is there a way to stub those out?
I'm using facebooker, which comes with a mock service:
# A mock service that reads the Facebook response from fixtures
# Adapted from http://gist.github.com/44344
#
# Facebooker::MockService.fixture_path = 'path/to/dir'
# Facebooker::Session.current = Facebooker::MockSession.create
But when I write a basic get test, it tries to redirect the browser to the facebook page for adding the app, which I assume indicates that the mocking isn't working.
test "loads respondent" do
Facebooker::Session.current = Facebooker::MockSession.create
get :index
puts #response.body # => <html><body>You are being redirected.</body></html>
end
I got this working with the latest version of facebooker (1.0.58):
# test_helper.rb
require 'facebooker/mock/session'
require 'facebooker/mock/service'
Facebooker::MockService.fixture_path = File.join(RAILS_ROOT, 'test', 'fixtures', 'facebook')
Obviously you will have to create the facebook directory in fixtures, or put it wherever. Inside you have to add a folder for each facebook method, and an xml file for the different types of responses you want to test for. I had to add facebook.users.getInfo and facebook.users.hasAppPermission. The easiest is just to add a file named default.xml with the example code from the facebook wiki for those actions.
# Controller test
test "facebook action" do
get :index, {:fb_sig_added => true}, :facebook_session => Facebooker::MockSession.create
assert_response :success
end
The fb_sig_added param is necessary as far as I can tell, because the internal facebooker logic checks the params directly before checking the session on that one. Which seems a bit wanky to me but maybe there's a reason for that.

Cucumber default_url_options[:host] everytime "www.example.com" even if specified in environtemnts/test.rb

I specified the default_url_options in my environments/test.rb with
config.action_mailer.default_url_options = { :host => "www.xyu.at" }
This is quite ok and in my cucumber story, where i test registration of users,
the user-activation link gets generated right
invitation_activation_url(1)
=> "www.xyu.at/signup/1231hj23jh23"
But when I try to follow the link provided in the email with following code in features/steps/user_steps.rb (using email-rspec from http://github.com/bmabey/email-spec/tree/master):
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_url(1) + '" in the email'
end
Here the url gets created with the default-host:
invitation_activation_url(1)
=> "www.example.com/signup/1231hj23jh23"
Can anybody help me? I don't get what I'm doing wrong....
Thanks!
EDIT:
It seems to do with the method
current_url
but I dont know where it comes from..?
EDIT:
And I have the right environment specified in my features/support/env.rb
ENV["RAILS_ENV"] ||= "test"
EDIT:
My temporary solution is, what edbond said,
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
but I dont want to name the domain explicit this way
(i specified it already in my environments/test.rb file - that way it wouldnt be dry)
Use :host option in your url.
invitation_activation_url(1, :host => "www.xyz.at")
=> "www.xyz.at/signup/1231hj23jh23"
EDIT:
You can parse email body and get link
mail = YourMailer.deliveries.last
email_html = Nokogiri::HTML mail.body.to_s
approve_link = email_html.at_css("a")["href"]
I know its years since this was posted.. but I had this issue and took me hours to decipher until I got it figured out.
You should use instead
When /^I follow the invitation link$/ do
When 'I follow "'+invitation_activation_path(1) + '" in the email'
end
the _url generates the URL path; whilst the _path generates the URI path.
web_steps.rb uses the URI to determine the current_url which it uses to work out the host.
from http://api.rubyonrails.org/classes/ActionDispatch/Routing.html
Routes can be named by passing an :as option, allowing for easy
reference within your source as name_of_route_url for the full URL and
name_of_route_path for the URI path.
from web_steps.rb
Then /^(?:|I )should be on (.+)$/ do |page_name| | # end
current_path = URI.parse(current_url).path | #
if current_path.respond_to? :should | # collection do
current_path.should == path_to(page_name) | # get 'sold'
else | # end
assert_equal path_to(page_name), current_path | # end
end |
end
You say that you edited config/environments/test.rb. Are you sure that your Cucumber features are actually executing in the 'test' environment?
I recently added Cucumber to a project I'm working on, and it seems to set itself up to use a 'cucumber' environment by default.
In features/support/env.rb in my project there is this:
ENV["RAILS_ENV"] ||= "cucumber"
So if your project is similar, then you will need to customize config/environments/cucumber.rb as well.
I'm not terribly familiar with Cucumber, so I can't say with certainty where exactly you'll have to apply this fix. But the problem is that the default_url_options is not set in another place where you're trying to generate your url...
So my advice is to first find out in what context the faulty url is being generated. Before or after it, just output self.class. That's the class you'll have to monkey-patch. For the example, let's say 'ClassName' was printed out.
When you have that, in your config/environments/test.rb, just add the attribute accessor and then set it to what you want:
class ClassName
cattr_accessor :default_url_options
# or mattr_ if it's a module
end
and then set it the same way as your actionmailer
ClassName.default_url_options = { :host => "www.xyu.at" }
This whole process can be useful as well when you want to generate urls in models or in other esoteric places (then you'll also need to include ActionController::UrlWriter).
One solution (based on info here) is to have a step definition like
Given /^the domain "(.+?)"$/ do |domain|
host! domain
end
and use it like
Given the domain "www.foo.com"
in features.
With that, though, I ran into issues where redirects were not followed. I tried applying this patch but had no luck.
I ended up using a very simple workaround in Cucumber's env.rb file:
# There is a bug in internal_redirect? when used with explicit (non-example.com) domains.
# This is a simple workaround but may break if we start specing external redirects.
# https://webrat.lighthouseapp.com/projects/10503/tickets/140-current_url-should-be-fully-qualified
class Webrat::Session
alias internal_redirect? redirect?
end
As mentioned in the comment, this may of course break with external redirects, but we have none.

Resources