At the moment, this is a simple project - just a couple of static pages. I'm developing a generic test framework but am struggling to differentiate between the different test options. I have added Rspec, Capybara, Faker, Factory Girl, Spring, and shoulda (though I'm not using the shoulda matchers at the moment).
I have this controller test file:
require 'rails_helper'
RSpec.describe StaticPagesController, type: :controller do
describe "GET #a_page" do
before(:each) { get :a_page }
it "returns http success" do
expect(response).to have_http_status(:success)
end
it "has a page title Static Site" do
expect(response).to have_title('Static Site')
end
end
end
When this runs through guard, it throws an error stack:
23:13:39 - INFO - Run all
23:13:39 - INFO - Running all specs
Running via Spring preloader in process 4498
Running via Spring preloader in process 4506
/home/steve/workspaces/static_site/db/schema.rb doesn't exist yet. Run `rake db:migrate` to create it, then try again. If you do not intend to use a database, you should instead alter /home/steve/workspaces/static_site/config/application.rb to limit the frameworks that will be loaded.
.F
Failures:
1) StaticPagesController GET #a_page has a page title Static Site
Failure/Error: expect(response).to have_title('Static Site')
NoMethodError:
undefined method `match' for nil:NilClass
Did you mean? catch
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/queries/title_query.rb:18:in `resolves_for?'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/document_matchers.rb:20:in `block in assert_title'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/simple.rb:144:in `synchronize'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/node/document_matchers.rb:19:in `assert_title'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/capybara-2.7.1/lib/capybara/rspec/matchers.rb:105:in `matches?'
# ./spec/controllers/static_pages_controller_spec.rb:34:in `block (3 levels) in <top (required)>'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb:18:in `call'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/command_wrapper.rb:38:in `call'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:191:in `block in serve'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:161:in `fork'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:161:in `serve'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:131:in `block in run'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:125:in `loop'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application.rb:125:in `run'
# /home/steve/.rvm/gems/ruby-2.3.1/gems/spring-1.7.1/lib/spring/application/boot.rb:19:in `<top (required)>'
# -e:1:in `<main>'
Finished in 0.029 seconds (files took 2.54 seconds to load)
2 examples, 1 failure
Failed examples:
rspec ./spec/controllers/static_pages_controller_spec.rb:33 # StaticPagesController GET #a_page has a page title Static Site
The first test runs OK and, without the second, I get a clean result. I've spent a lot of time going over my config and it looks OK. I have also looked at the docs and some support sites.
Can anybody help out?
Capybara matchers need to be called against a html/xml document element (or a string that parses into a document), not against a response object. By default Capybaras matchers are normally only available in feature and view specs (not controller) was there a particular reason you included them into controller specs? Verifying a pages title really should lean more towards being a view spec than controller (by default views aren't rendered in controller specs - https://www.relishapp.com/rspec/rspec-rails/docs/controller-specs).
As #Thomas Walpole pointed out, Capybara is not enabled in controller specs by default. One option is to change the type of the test to feature (type: :feature).
To make Capybara's built-in matcher have_title work, you need to use visit from Capybara's API, not get from ActionDispatch::IntegrationTest.
Related
Rails 6.1.3.1
Rspec
basic behavior spec code:
describe "index" do
it "should show me the list" do
visit dashboard_targets_path
end
end
the routes file
namespace :dashboard do
resources :targets
end
error shows me the exception, but strangely it appears as if it isn't calling through to the app, just fails right in my test code:
1) interaction for Dashboard::TargetsController index should show me the list
Failure/Error: visit dashboard_targets_path
NoMethodError:
undefined method `empty?' for nil:NilClass
# ./spec/system/dashboard/targets_behavior_spec.rb:16:in `block (3 levels) in <top (required)>'
# /Users/jason/.rvm/gems/ruby-2.6.6/gems/webmock-3.12.2/lib/webmock/rspec.rb:37:in `block (2 levels) in <main>'
# /Users/jason/.rvm/gems/ruby-2.6.6/gems/rspec-wait-0.0.9/lib/rspec/wait.rb:46:in `block (2 levels) in <main>'
it seems to be failing inside the test code, if I drop into the debugger there and run dashboard_targets_path directly I also get the same exception, so the problem is just using the helper within the TEST ENVIRONMENT
within the dev environment, this function works
the problem here was the the config/environments/test.rb file does have default_url_options.
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
when you hit errors that disappear into the Rails gems, a good way to debug them is like so:
begin
// your failing code here
rescue StandardError => e
puts e.backtrace
byebug
puts e.inspect
raise (e)
end
WARNING: do not leave this code in your app or check it in unless you explicitly want to use exception handling for flow control (not recommended!). This is recommended ONLY for debugging purposes.
here you will see the full backtrace to the line number in the Gem where it is failing. when debugging Gems be careful— remember to un-do any changes you make and know that your monkey-patching inside of the Gem code doesn't affect your production code.
I have the following feature tests passing:
require 'rails_helper'
describe "Create a quetsion", type: :feature do
let(:question) { build(:question) }
before do
login_as create(:user, :teacher)
exercise = create(:exercise)
visit new_exercise_question_url(exercise.id)
end
context "whit valid attributes" do
subject do
fill_in "question_score", with: question.score
fill_in "question_description", with: question.description
click_on "Criar"
end
it "create the question" do
expect{ subject }.to change(Question, :count).by(1)
expect(page).to have_current_path(question_path(Question.first.id))
end
end
context "whit invalid attributes" do
subject do
click_on "Criar"
end
it "doesn't create the exercise" do
expect{ subject }.to change(Question, :count).by(0)
expect(page).to have_selector("div.alert.alert-danger")
end
end
end
That works fine, unless I add js: true. In this case, I have the following errors:
1) Create a quetsion whit valid attributes create the question
Failure/Error: fill_in "question_score", with: question.score
Capybara::ElementNotFound:
Unable to find field "question_score"
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:44:in `block in find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/base.rb:85:in `synchronize'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/finders.rb:33:in `find'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/node/actions.rb:85:in `fill_in'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:735:in `block (2 levels) in <class:Session>'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (4 levels) in <top (required)>'
# ./spec/features/create_a_question_spec.rb:20:in `block (3 levels) in <top (required)>'
2) Create a quetsion whit invalid attributes doesn't create the exercise
Got 0 failures and 2 other errors:
2.1) Failure/Error: visit new_exercise_question_url(exercise.id)
Capybara::Poltergeist::StatusFailError:
Request to 'http://www.example.com/exercises/1/questions/new' failed to reach server, check DNS and/or server status
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:376:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:35:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:97:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:240:in `visit'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/dsl.rb:52:in `block (2 levels) in <module:DSL>'
# ./spec/features/create_a_question_spec.rb:8:in `block (2 levels) in <top (required)>'
2.2) Failure/Error: #socket.send(command.id, command.message, receive_timeout) or raise DeadClient.new(command.message)
Capybara::Poltergeist::DeadClient:
PhantomJS client died while processing {"id":"2928bf26-2dd8-45d7-8822-41b2923fd40d","name":"reset","args":[]}
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/server.rb:38:in `send'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:369:in `command'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/browser.rb:224:in `reset'
# /usr/local/rvm/gems/ruby-2.3.1/gems/poltergeist-1.13.0/lib/capybara/poltergeist/driver.rb:183:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/session.rb:109:in `reset!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `block in reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reverse_each'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara.rb:334:in `reset_sessions!'
# /usr/local/rvm/gems/ruby-2.3.1/gems/capybara-2.10.1/lib/capybara/rspec.rb:21:in `block (2 levels) in <top (required)>'
I checked, whit js: true my page html isn't written too (instead the desired HTML, I only receive <html><head></head><body></body></html>).
I added poltergeist gem to test javascritp, and configured it in my rspec rails_helper.rb file:
require 'capybara/poltergeist'
Capybara.javascript_driver = :poltergeist
PhantomJS is already instaled and available in my $PATH. I'm completelly out of ideas, what can be happening here?
There are a number of potential issues here.
If you have set Capybara.server = :puma, make sure you're not running puma 3.7.0 which has a bug in it that will be fixed when 3.7.1 is released. For now use 3.6.9
If this is the first time you're setting up js tests make sure you have setup and correctly configured DatabaseCleaner so that you are no running in transaction mode for js: true tests. See https://github.com/teamcapybara/capybara#transactions-and-database-setup and https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example for the recommended configuration
Once you have #1 and #2 worked out then you'll need to look at your tests and deal with the fact that after a click/click_on type action you need to check for a visible page change to ensure the action has completed before you move on. This is because clicks happen but the actions those clicks trigger can occur asynchronously. In your current example that means you would need something like
...
click_on "Criar"
expect(page).to have_text("Question created!") #whatever message is shown when the action has completed, or check the page etc.
Without something like that the change check will fail because it will check the count before the actual actually occurs.
Default to _path helpers rather than _url helpers when there's no need for a specific host name (99.9% of the time). This lets Capybara fill_in the correct host/port for the server being run. To visit _url helpers there are a number of configuration parameters that need to be carefully set.
Note: It's generally not best practice to be checking database counts in feature tests, they should generally be limited to checking on visible page changes.
I'm currently moving some feature tests from Minitest to RSpec. My last problem is with reaching the Sidekiq WebUI which I enabled in my routes.rb
require 'sidekiq/web'
require 'admin_constraint'
Rails.application.routes.draw do
mount Sidekiq::Web => '/sidekiq', constraints: AdminConstraint.new
...
end
The ressource is admin-only protected, which I now want to test. I have two questions
1) Contemplating about the test again made me think, whether it would be better formulated as a routing test. What do you think? Keep in mind that it routes to an external Engine which might make it kinda more complicated. The feature test seemed an easy way around this (simply test whether the ressource is reachable).
2) In Minitest I could do e.g. the following, which was working fine
require_relative '../test_helper'
feature 'Sidekiq dashboard' do
scenario 'Dashboard cannot be reached as guest user' do
assert_raise ActionController::RoutingError do
visit sidekiq_web_path
end
end
scenario 'Dashboard cannot be reached as regular user' do
login_as_user
assert_raise ActionController::RoutingError do
visit sidekiq_web_path
end
end
scenario 'Dashboard can be reached as admin' do
login_as_admin
assert_nothing_raised do
visit sidekiq_web_path
end
end
end
I tried to convert it to RSpec directly like so
scenario 'Dashboard can be reached as user' do
login_as_user
expect {
visit sidekiq_web_path
}.to raise_error(ActionController::RoutingError)
end
which produces the following error
Failures:
1) Sidekiq dashboard Dashboard cannot be reached as regular user
Got 1 failure and 1 other error:
1.1) Failure/Error:
expect {
visit sidekiq_web_path
}.to raise_error(ActionController::RoutingError)
expected ActionController::RoutingError but nothing was raised
# ./spec/features/sidekiq_monitoring_spec.rb:12:in `block (2 levels) in <top (required)>'
1.2) Failure/Error: raise ActionController::RoutingError, "No route matches [#{env['REQUEST_METHOD']}] #{env['PATH_INFO'].inspect}"
ActionController::RoutingError:
No route matches [GET] "/sidekiq"
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:38:in `call_app'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:20:in `block in call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/rack/logger.rb:20:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/request_store-1.3.2/lib/request_store/middleware.rb:9:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/methodoverride.rb:22:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/runtime.rb:18:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/lock.rb:17:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/sendfile.rb:113:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/engine.rb:518:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/railties-4.2.7.1/lib/rails/application.rb:165:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:66:in `block in call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:50:in `each'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/urlmap.rb:50:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/capybara-2.12.0/lib/capybara/server.rb:43:in `call'
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/rack-1.6.5/lib/rack/handler/webrick.rb:88:in `service'
# ------------------
# --- Caused by: ---
# Capybara::CapybaraError:
# Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true
# /home/blubber/.rvm/gems/ruby-2.3.1/gems/capybara-2.12.0/lib/capybara/session.rb:129:in `raise_server_error!'
Why does this not work in RSpec?
Thanks in advance, all the best,
Andi
When testing with a JS capable driver (poltergeist, etc) Capybara runs the app in a separate server thread. Because of that errors raised in the app don't automatically get seen in the test code. To overcome that Capybara stores any errors raised in the server and, due to the asynchronous nature of commands when using a JS capable driver, re-raises them in the test code the next time it attempts to interact with Capybara. Because of that you would need to have a second interaction inside the block you expect to raise the error
expect {
visit sidekiq_web_path
expect(page).to have_text("Something") # the error will actually raise here
}.to raise_error(ActionController::RoutingError)
I have no clue why it was working for you like that with minitest, and would have to go look at the Minitest code to attempt to figure out why. I would expect your tests to have worked correctly if you were running those tests with the rack_test driver since it doesn't run a separate thread and just calls directly into the app (you claim not to have changed that though).
Note: This really isn't the sort of thing that you should be checking in a feature test, instead it would be better to check for what is displayed on the page when permission is denied/allowed instead of a specific error class that is raised
I'm running a feature spec to test some Rails code. The spec runs fine however, there is some js that needs to be tested.
As per the documentation, I've added the :js => true flag to the scenario. However, when I do this, I get the following error:
Failures:
1) User can check off items as being owned user can check off an item on the checklist
Failure/Error: Unable to find matching line from backtrace
Errno::EADDRINUSE:
Address already in use - bind(2) for 127.0.0.1:3000
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/rack-1.6.0/lib/rack/handler/webrick.rb:32:in `new'
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/rack-1.6.0/lib/rack/handler/webrick.rb:32:in `run'
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/capybara-2.4.4/lib/capybara.rb:173:in `run_default_server'
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/capybara-2.4.4/lib/capybara.rb:359:in `block (2 levels) in <top (required)>'
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/capybara-2.4.4/lib/capybara/server.rb:70:in `call'
# /Users/agazoom/.rvm/gems/ruby-2.2.1#kollista/gems/capybara-2.4.4/lib/capybara/server.rb:70:in `block in boot'
The test is listed below. This is strange because the thing runs fine when I remove the js flag:
scenario "user can check off an item on the checklist", :js => true do
visit root_path
expect(page).to have_title("agazoom")
end
I did some experimenting and discovered that when I turn off webkit, the tests work. Basically, it looks like Capybara was trying to use the same port as webkit and failing when using js. Don't ask me the details as to why or how.
So, I just changed the port on which Capybara runs in 'rails_helper':
Capybara.configure do |config|
.
.
config.server_port = 3001
end
I have the following in a feature spec:
it "shows the page" do
Project.any_instance.stub(:price_all)
login_user
gso = create(:gs_option)
gso.gs_collector.collector.project.update(user_id: #user.id)
visit edit_gs_option_path(gso)
end
Yet, it always fails because the price_all method on Project is not being stubbed. The failure trace contains the following:
# ./app/models/project.rb:430:in `price_all'
How do I stub the price_all method on the Project class?
I've tried stub(:price_all).with(anything()) and stub(:price_all).with(any_args()), but it doesn't change the failure message.
Here's the full failure:
1) GS Options page shows the page
Failure/Error: visit edit_gs_option_path(gso)
NoMethodError:
undefined method `id' for nil:NilClass
# ./app/models/collector.rb:435:in `price_item'
# ./app/models/gs_collector.rb:279:in `price_out'
# ./app/models/collector.rb:260:in `price_out_all'
# ./app/models/project.rb:430:in `price_all'
# ./app/controllers/application_controller.rb:187:in `get_totals'
# ./app/controllers/gs_options_controller.rb:6:in `edit'
# ./spec/features/gs_options_spec.rb:10:in `block (2 levels) in <top (required)>'
This stubbing feature started working when I upgraded to rspec-rails 3.0.0.beta.