I'm working on a Rails 4 app and i want to write some tests for BrainTree:
using rspec-rails (2.14.0) and capybara (2.1.0) in Rails 4.0.0
The problem is with on the route, in the form for Braintree i pass a :url
<%= form_for :customer, :url => Braintree::TransparentRedirect.url do |f| %>
Now when i run a feature test like this one:
it 'should make new payment info' do
login
visit new_customer_path
page.fill_in 'customer_credit_card_number', :with => '4111111111111111'
page.fill_in 'customer_credit_card_expiration_date', :with => '01/25'
page.fill_in 'customer_credit_card_cvv', :with => '400'
page.click_button 'Save Payment Info'
page.should have_content('Payment Info Confirmation')
page.should have_content('411111******1111')
end
i get a error on the route:
Failure/Error: page.click_button 'Save Payment Info'
ActionController::RoutingError:
No route matches [POST] "/merchants/fvn6vfc5ptyg2xrp/transparent_redirect_requests"
I've also tried this in a controller test (with render_views):
it 'should make new payment info' do
sign_in_as_user
visit new_customer_path
page.fill_in 'customer_credit_card_number', :with => '4111111111111111'
page.fill_in 'customer_credit_card_expiration_date', :with => '01/25'
page.fill_in 'customer_credit_card_cvv', :with => '400'
page.click_button 'Save Payment Info'
save_and_open_page
page.should have_content('Payment Info Confirmation')
page.should have_content('411111******1111')
end
Same error on the route...
In development env in the browser it works fine, i looks like the :url option in my form gets ignored by capybara? I wonder if anybody can help me with this?
I've also found these examples apps for Braintree with Rails: https://github.com/braintree/braintree_ruby_examples/blob/master/rails3_tr_devise/spec/controllers/customer_controller_spec.rb when i run the tests on that project it works.. maybe my problem has to do with the version of Rails and rspec?
many thanks in advance!!
I actually cover this exact scenario in my Multitenancy with Rails book.
The difference between your tests and Braintree's project's tests are that your tests are Capybara features and theirs' are controller specs.
I mention this relevant part of Capybara's README within the book:
RackTest is Capybara's default driver. It is written in pure Ruby and does not
have any support for executing JavaScript. Since the RackTest driver interacts
directly with Rack interfaces, it does not require a server to be started.
However, this means that if your application is not a Rack application (Rails,
Sinatra and most other Ruby frameworks are Rack applications) then you cannot
use this driver. Furthermore, you cannot use the RackTest driver to test a
remote application, or to access remote URLs (e.g., redirects to external sites,
external APIs, or OAuth services) that your application might interact with.
The way I get around it is that I wrote a gem called fake_braintree_redirect which inserts a piece of middleware into the request stack during the test environment to capture these requests and respond appropriately. The middleware is added to the stack using an initializer block defined within application.rb, like this:
initializer 'middleware.fake_braintree_redirect' do
if Rails.env.test?
require 'fake_braintree_redirect'
config.middleware.use FakeBraintreeRedirect
end
end
This takes Braintree out of the equation completely and returns a successful response whenever you send data to it.
Alternatively, if you really want to test against Braintree's sandbox, you could switch to the JavaScript driver by tagging your scenario as:
scenario "foo", :js => true
Related
I'm trying to use capybara on a ruby on rails application to do some content testing, as well I'm using the devise gem to implement user authentication. I'm having trouble logging into my application to perform my test cases.
Initially, my scenario was as follows:
scenario "User arrives at main page" do
visit "purchase_orders#index"
page.should have_content("All")
# some more tests and checks
end
Where purchase_orders#index is the authenticated root, where a user's purchase orders are shown.
But when I was running the tests, I was getting the following error :
expected to find text "All" in "Log in to manage your orders * Email * Password Forgot your password? Remember me Sign up • Didn't receive confirmation instructions? About Us • Contact Us •
which tells me that its not getting past the log in page. I next tried adding the following to my scenario, before running the tests, to make it log in:
visit "purchase_orders#index"
fill_in('Email', :with => 'username#gmail.com')
fill_in('Password', :with => 'password')
click_button('Log in')
where username and password are actual created accounts, but again it fails and doesn't get past the sign in page. Finally, I tried adding a before(:each) method, as follows, to attempt to sign users in for test cases:
before(:each) do
visit "users/sessions#new"
fill_in('Email', :with => 'nico.dubus17#gmail.com')
fill_in('Password', :with => 'password')
click_button('Log in')
end
which, again, did not work for me. So my question is: What is the best practice and syntax for getting past the sign in page, and into the actual application? I've looked for documentation and answers on this, but haven't found anything.
Thank you!
Found an answer. I installed the warden gem to (gem 'warden') and factory girl gem (gem "factory_girl_rails", "~> 4.0") and ran a bundle install. I then created a user fixture with factory girl as follows in a factory.rb file in the spec folder:
# This is a user factory, to simulate a user object
FactoryGirl.define do
factory :user, class: User do
first_name "John"
last_name "Doe"
email "nico_dubus#hotmail.com"
encrypted_password "password"
end
end
in my rails helper file, I added this line to be able to use FactoryGirl's methods without calling it on the class every time:
config.include FactoryGirl::Syntax::Methods
Afterwards, I added these lines to the top of my capybara test page:
include Warden::Test::Helpers
Warden.test_mode!
Finally, I built a user object to use within the scope of the test:
user = FactoryGirl.build(:user)
And whenever I need to log in, I use warden's log in method
login_as(user, :scope => :user)
And voila!
Solution
This worked. The main essence is that I have to set the Capybara.server_port and Capybara.app_host and sign in manually in the sign in form. Capybara.app_host cannot be set with a dynamic subdomain unless its declared in a variable. All urls has to be hard coded.
require 'spec_helper'
feature 'customer' do
let(:user) {FactoryGirl.create(:user)}
let(:firm) {user.firm}
let(:customers) {"http://#{firm.subdomain}.lvh.me:31234/customers"}
let(:root_url) {"http://#{firm.subdomain}.lvh.me:31234/"}
before(:all) do
Capybara.server_port = 31234
sub = firm.subdomain
Capybara.app_host = root_url
end
def sign_in_on_js
visit root_url
fill_in "Email", :with => user.email
fill_in "Password", :with => "password"
click_button "Sign in"
page.should have_content("Signed in successfully.")
end
scenario "make new", js: true do
sign_in_on_js
visit customers
page.should have_content("Add new customer")
find("#dialog_customer").click
page.should have_content("Create new customer")
end
end
Original question
I am making a multitenant app in rails. There is going to be a lot of javascript. But, I cant get the testing to work.
When not running :js = true every thing works. The problem arises in specs like this one
let(:customers) {"http://#{firm.subdomain}.lvh.me:3003/customers"}
scenario "Statistics select", :js => true do
visit customers
page.should have_content("Add new customer")
end
The poltergeist web driver for capybara cannot find the url and returns a blank page
Failure/Error: page.should have_content("Add new customer")
expected there to be text "Add new customer" in ""
I have this in my spec_helper.rb
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, :debug => true)
end
Poltergeist and phantomjs try to deliver. I get this output
{"name"=>"set_debug", "args"=>[true]}
{"response"=>true}
{"name"=>"visit", "args"=>["http://subdomain2.lvh.me:3003/statistics"]}
poltergeist [1362522132943] state default -> loading
{"response"=>{"status"=>"fail"}}
Do I need to have a server running during testing to make this work?
I've tried selenium and capybara-webkit, but phantomjs has gotten closest to success.
I have also tried to change the hosts file in different ways( maybe not correct )
Any tips on setup are welcome!
Update
Starting to get desperate. I now start the rails server
rails s -e test -p 3001
and then run my tests.
Now I get redirected to the sign in page. I have this in the specs
before(:each) do
login_as(user, :scope => :user)
end
How can I sign in the test user on the rails test server without going trough the sign in process manually for every spec
Capybara already starts a server for you, to quote the docs:
Some Capybara drivers need to run against an actual HTTP server. Capybara takes care of this and starts one for you in the same process as your test, but on another thread. Selenium is one of those drivers, whereas RackTest is not.
Within your test you can use the visit method with a relative url, for example:
visit("/statistics")
Capybara will direct this request to the server it just started for this test.
When your want to use an absolute url within your test, you can, but you should also specify the port the server is running on. This port is being randomly chosen during the test. Some drivers have a method available to retrieve the port number.
For example when you use the Capybara-Webkit driver:
Capybara.current_session.driver.server_port
To visit an absolute url you can then use:
port_number = Capybara.current_session.driver.server_port
visit("http://127.0.0.1:#{port_number}/statistics")
Within the test specs probably a method login_as won't work. You have to log in with a few simple steps. For example:
before(:each) do
visit "/login"
fill_in "Email", :with => "my#email.com"
fill_in "Password", :with => "secret"
click_button "Login"
end
To test multiple subdomains you can set the Capybara.app_host. Take a look at this question for a detailed explanation.
UPDATE
Capybara 2 includes a nice feature called always_include_port which will automatically add the port number the server is running on.
Capybara.always_include_port = true
So instead of
visit("http://127.0.0.1:#{port_number}/statistics")
you can now use
visit("/statistics")
and it will automatically connect to http://127.0.0.1:#{port_number}/statistics.
If you want to test multiple subdomains with Capybara.app_host, you could use a domain name which always resolves to 127.0.0.1 for example lvh.me.
For example, if you specify Capybara.app_host = "http://example.lvh.me" it will run the tests using the example subdomain.
I have an application using rails 3.2 and Devise. I have a request test suite using rspec and Capybara.
I have attempted to shift to using the Warden test helpers in my sign in helper rather than having Capybara fill in the sign in form and submit it. Due to the size and complexity of my test suite, this results in over a minute and a half of savings in my test runtimes.
In my configuration is:
RSpec.configure do |config|
config.include Warden::Test::Helpers, :type => :request
config.after :each do
Warden.test_reset!
end
end
And in a Context:
let!(:current_user) { FactoryGirl.create(:user) }
background do
login_as(current_user, :scope => :user)
end
However, when running the test suite using these configurations nearly every test run has a different randomly failing request spec due to the page presenting as though the user were not logged in. (Specifically, links controlled by a user_signed_in? conditional)
My question: Has anyone else encountered such unreliability and how can it be mitigated?
The absolute easiest thing to do is just use the sign_in form in your before :each block. You need the test helpers in Controller specs, because it's isolated from devise. You don't have that problem in an integration test.
Though an old post I also have this problem with Rails 4.1 and Warden 1.2.3. I noticed that there is a newer version of Warden with work on the test helpers. Anyone success with the newer version?
Btw the behavior that I observe is that Warden does not always successfully login via the login_as helper. This results in my app finding itself on the login page instead of the place it thought it ought to be.
After digging fairly deeply on this issue, I've come to an impasse between my understanding of the documentation and my results.
According to https://www.relishapp.com/rspec/rspec-rails/v/2-8/docs/routing-specs/route-to-matcher, we should be able to write the following:
#rspec-rails (2.8.1)
#rspec (>= 1.3.1)
#rspec-core (~> 2.8.0)
# routing spec
require "spec_helper"
describe BusinessUsersController do
describe "routing" do
it "routes to some external url" do
get("/business_users/7/external_url").should route_to("http://www.google.com")
end
end
end
# routes.rb
BizeebeeBilling::Application.routes.draw do
resources :business_users do
member do
get "external_url" => redirect("http://www.google.com")
end
end
end
Running this spec produces the following results:
Failures:
1) BusinessUsersController routing routes to some external url
Failure/Error: assert_routing "/business_users/7/external_url", "http://www.google.com"
ActionController::RoutingError:
No route matches "/business_users/7/external_url"
# ./spec/routing/business_users_routing_spec.rb:19:in `block (3 levels) in <top (required)>'
I have not been able to find anyone reporting this specific issue anywhere.
Added detail: the route is resolved perfectly well when testing manually.
Routing specs/tests specialize in testing whether a route maps to a specific controller and action (and maybe some parameters too).
I dug into the internals of Rails and Journey a bit. RSpec and Rails (basically, some details left out) use Rails.application.routes.recognize_path to answer the question "is this routable?"
For example:
$ rails console
> Rails.application.routes.recognize_path("/business_users/1", method: "GET")
=> {:action=>"show", :controller=>"business_users", :id=>"1"}
However, there's no controller on the other end of /business_users/1/external_url. In fact, to perform the redirect, Rails has created an instance of ActionDispatch::Routing::Redirect, which is a small Rack application. No Rails controller is ever touched. You're basically mounting another Rack application to perform the redirection.
To test the redirect, I recommend using a request spec instead (a file in spec/requests). Something like:
require "spec_helper"
describe "external redirection" do
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
This tests the route implicitly, and allows you to test against the redirection.
Andy Lindeman has the correct answer. However, you don't have to put the spec in spec/requests, you can keep it in spec/routing and be explicit with the metadata "type": describe 'my route', type: :request do
I was running into a similar case where I was trying to test a series of routes, some which should redirect and some which shouldn't. I wanted to keep them in a single routing spec, since that was the most logical way to group them.
I tried using describe: 'my route', type: request, but found that not to work. However, you can include RSpec::Rails::RequestExampleGroup in your spec context to gain access to the request spec methods. Something like:
describe "My Routes" do
context "Don't Redirect" do
it "gets URL that doesn't redirect" do
get("business_users/internal_url").should route_to(controller: "business_users", action: "internal_url_action")
end
end
context "Redirection" do
include RSpec::Rails::RequestExampleGroup
it "redirects to google.com" do
get "/business_users/1/external_url"
response.should redirect_to("http://www.google.com")
end
end
end
The simplest way to test external redirects is to use an integration test:
test "GET /my_page redirects Google" do
get "/my_page"
assert_redirected_to "https://google.com"
end
You test needs to be under your test/integration directory or the equivalent directory where the integration tests should go.
I think you want the redirect_to matcher.
I have the following steps in a capybara/rspec integration test, that is simply trying to sign up a new member.
visit new_member_registration_path
fill_in('Name:', :with => 'Rob Doe' )
fill_in('member_email', :with => 'rob#smith.com' )
fill_in('member_email_confirmation', :with => 'rob#smith.com' )
fill_in('member_password', :with => 'secret')
fill_in('Company or Venue Name:', :with => 'Rob Inc.')
fill_in('Contact Number:', :with => '040544404440')
click_button('Sign up')
save_and_open_page
For some reason the 'email' and 'password' data is not being passed to the DeviseRegistrations controller (it is blank when viewing the test log) and therefore causing the validation to fail. However up until the save_and_open_page there is no rspec errors (so those fields are being filled in).
What am I missing? Do I need to subclass the DeviseRegistrations controller?
Tested on Rails 3.0.7 with rack-test 0.5.7 and rails 3.1rc1 and rack-test 0.6.0
Assuming you have debugger in your Gemfile, here's how you can use it. (This assumes you're using the Rack driver for Capybara.)
# test.rb
visit new_member_registration_path
fill_in('Name:', :with => 'Rob Doe' )
debugger
The terminal will stop your script and wait for you to do something.
# Terminal
/file/path/to/you/test.rb:12
fill_in('Name:', :with => 'Rob Doe' )
(rdb:1)
Open up an IRB session here:
(rdb:1) irb
You can do any RSpec or Capybara method here:
>> current_path.should == 'foo/bar'
Try submitting the form at this point:
>> click_button "Sign Up"
>> save_and_open_page
See what error messages Devise gave to you on the resulting page. With the Rack driver, you won't see the fields being filled in. In that case, you might want to try using the Selenium driver
# test.rb
Capybara.default_driver = :selenium
visit new_member_registration_path
However, you can't drive Capybara from IRB using the Selenium driver. You will, though, be able to see what form values Selenium is putting into your form. Since things happen quickly with Selenium, you can use debugger to pause the test, while you inspect the page that Selenium opened up in your browser.
The problem was in the application layout file. I had another (albeit hidden) form that was posting the blank form fields.
After I created a blank project and saw that it worked perfectly, I peeled back all the potential parts of my app until I found the culprit.
So the answer to the question is, no, a custom devise controller is not required when you are using custom devise views.