I recently switched a very simple rails app from rspec to minitest. I also use capybara and factory_girl.
I have 3 separate integration test files, all of which involve logging the user in using something along the lines of:
before(:each) do
user = Factory(:user)
visit login_path
fill_in "Email", :with => user.email
fill_in "Password", :with => user.password
click_button "Log in"
end
After I switched to minitest, it seems as if the sessions ceased to tear down after each test. For instance, I would test login using the above code in a test file named "users_integration_test.rb" and when it begins running another test file, say "sessions_integration_test.rb", the user is already logged in before I can log in again using the above code.
My question is: Is this an intentional difference between rspec and minitest, and I simply need to logout the user after each test? Or did I make a mistake setting up minitest?
I am using the same minitest_helper file as in the Minitest Railscast.
I don't know the difference but below code may work.
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"
class IntegrationTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
after do
reset_session!
end
register_spec_type(/integration$/, self)
end
class HelperTest < MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
register_spec_type(/Helper$/, self)
end
I got it to tear down correctly with this. Hope it helps!
Mr. Maeshima's answer may very well work too. I have not tried.
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "minitest/autorun"
require "capybara/rails"
require "active_support/testing/setup_and_teardown"
Dir[Rails.root.join("test/support/**/*.rb")].each {|f| require f}
DatabaseCleaner.strategy = :truncation
class IntegrationTest < MiniTest::Spec
include Rails.application.routes.url_helpers
include Capybara::DSL
register_spec_type(/integration$/, self)
Capybara.javascript_driver = :selenium
after do
DatabaseCleaner.clean # Truncate the database
Capybara.reset_sessions! # Forget the (simulated) browser state
Capybara.use_default_driver # Revert Capybara.current_driver to Capybara.default_driver
end
end
class HelperTest < MiniTest::Spec
include ActiveSupport::Testing::SetupAndTeardown
include ActionView::TestCase::Behavior
register_spec_type(/Helper$/, self)
end
Related
I learned how to test image file uploading from this excellent article:
https://jeffkreeftmeijer.com/2010/2014/using-test-fixtures-with-carrierwave/
Adapting those ideas for my app, I've got this test that works:
test "uploads an image" do
pic = Picture.create(:image => fixture_file_upload('/files/DJ.jpg','image/jpg'), :user => User.first)
assert(File.exists?(pic.reload.image.file.path))
end
I would like to test the same thing for the user interface, so I would think that this test would also pass:
test "create new picture" do
capybara_login(#teacher_1)
click_on("Upload Pictures")
fill_in "picture_name", with: "Apple"
attach_file('picture[image]', Rails.root + 'app/assets/images/apple.png')
check("check_#{#user_l.id}")
check("check_#{#admin_l.id}")
click_on ("Create Picture")
#new_pic = Picture.last
assert_equal "Apple", #new_pic.name
assert #new_pic.labels.include?(#user_l)
assert #new_pic.labels.include?(#admin_l)
assert #teacher_1, #new_pic.user
assert File.exists?(#new_pic.reload.image.file.path)
end
But it fails on the last line, asserting the existence of the file.
Below are the parts of my test_helper that I thought would be relevant. I can show the whole test_helper if someone thinks it necessary.
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require "minitest/reporters"
require 'capybara/rails'
Minitest::Reporters.use!
CarrierWave.root = 'test/fixtures/files'
class CarrierWave::Mount::Mounter
def store!
# Not storing uploads in the tests
end
end
class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
include ApplicationHelper
include ActionDispatch::TestProcess
CarrierWave.root = Rails.root.join('test/fixtures/files')
def after_teardown
super
CarrierWave.clean_cached_files!(0)
end
end
Everything appears to be working as expected in development and production.
Thank you in advance for any insight.
I'm quite desperate since moving our test suite from Minitest to RSpec. All the controller and model tests run fine so far, but since trying to port (formerly passing/working) feature tests like the following I ran into trouble...
feature 'Create place' do
scenario 'create valid place as user' do
login_as_user
visit '/places/new'
fill_in_valid_place_information
click_button('Create Place')
visit '/places'
expect(page).to have_content('Any place', count: 1)
end
...
def fill_in_valid_place_information
fill_in('place_name', with: 'Any place')
fill_in('place_street', with: 'Magdalenenstr.')
fill_in('place_house_number', with: '19')
fill_in('place_postal_code', with: '10963')
fill_in('place_city', with: 'Berlin')
fill_in('place_email', with: 'schnipp#schnapp.com')
fill_in('place_homepage', with: 'http://schnapp.com')
fill_in('place_phone', with: '03081763253')
end
end
Unfortunately this does not lead to a DB commit which makes the test fail. It does not fail if i pry into the test and manually create the requested place. I tried different methods in order to trigger the button but nothing worked so far.
This is how my rails_helper.rb looks like:
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'spec_helper'
require 'rspec/rails'
require 'capybara/rspec'
require 'capybara/rails'
require 'capybara/poltergeist'
require 'pry'
def validate_captcha
fill_in 'captcha', with: SimpleCaptcha::SimpleCaptchaData.first.value
end
def login_as_user
user = create :user, email: 'user#example.com'
visit 'login/'
fill_in 'sessions_email', with: 'user#example.com'
fill_in 'sessions_password', with: 'secret'
click_on 'Login'
end
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
ActiveRecord::Migration.maintain_test_schema!
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, phantomjs_options: ['--ignore-ssl-errors=true'])
end
Capybara.javascript_driver = :poltergeist
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.clean
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, no_transaction: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.include Capybara::DSL
config.include Rails.application.routes.url_helpers
config.fixture_path = "#{::Rails.root}/spec/fixtures"
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
Does anyone have a clue about a possible cause? Gem versions:
capybara 2.12.0
rspec 3.5.0
rails 4.2.7.1
best and thanks,
Andi
--- Update
I added fill_in_valid_place_information method
This is how the test fails with or without a Capybara JS driver enabled (shouldn't matter in case of this test as the feature does not use any JS). Unfortunately it doesn't give any real hints to work with...
1) Create place create valid place as user
Failure/Error: expect(page).to have_content('Any place', count: 1)
expected to find text "Any place" 1 time but found 0 times in "KIEZ KARTE Find places Here comes a list of all POIs currently available in our database. If you are looking for a specific location please enter parts of its descriptive features into the 'Search' field. Search: Name Postal code Categories No data available in table"
Timeout reached while running a *waiting* Capybara finder...perhaps you wanted to return immediately? Use a non-waiting Capybara finder. More info: http://blog.codeship.com/faster-rails-tests?utm_source=gem_exception
--- Update 2
I found the issue which is not capyara-related. Actually I forgot to transfer a stub response for an API we're calling. Thanks everybody for participating in my struggle!
There are a number of potential issues in your test that could be causing what you are seeing, it would be easier to narrow down in the future if you included the actual error message(s) your test produces.
Your scenarion/feature isn't tagged with :js metadata to activate using the Capybara driver. It's possible you've specified Capybara.default_driver somewhere, but if so then your DatabaseCleaner config is wrong
Use the recommended DatabaseCleaner configuration from https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example . The driver name detection will work if you have specified Capybara.default_driver as mentioned in #1 and also with the :js/:driver metadata usage pattern. Additionally, the append_after/after difference is important to reduce test flakiness
Your login_as_user method needs to verify the login has completed before returning. This is because click_on 'Login' can trigger asynchronously and return before the login actually occurs. This leads to the visit you call immediately following aborting the login, preventing the session cookie from being sent, and ending up with a non logged in user when you expected the user to be logged in. To fix this you need something like
def login_as_user
...
click_on 'Login'
expect(page).to have_text('You are now logged in!') #whatever message is shown on successful login, or use have_css with some element on the page that only exists when a user is logged in (user menu, etc)
end
The same issue exists between click_button('Create Place') and visit '/places' where the visit can effectively cancel the effects of the button click
Getting the following when running my capybara rails tests with "rake test". Problem seems to be url helpers I'm using in my application.html.erb:
DashboardLoginTest#test_login_and_check_dashboard:
ActionView::Template::Error: arguments passed to url_for can't be handled. Please require routes or provide your own implementation
app/views/layouts/application.html.erb:29:in `_app_views_layouts_application_html_erb__938277815294620636_4045700'
test/integration/dashboard_login_test.rb:6:in `block in <class:DashboardLoginTest>'
Here's line 29 in application.html.erb that its complaining about:
<%= link_to("asdf", root_path, {:class => 'brand'}) %>
Here's what the test looks like:
test "login and check dashboard" do
visit("/")
assert page.has_content?("welcome")
end
Here's my test_helper.rb:
ENV["RAILS_ENV"] ||= "test"
require File.expand_path('../../config/environment', __FILE__)
require 'rails/test_help'
require 'factory_girl'
require 'capybara/rails'
include Warden::Test::Helpers
Warden.test_mode!
def main_app
Rails.application.class.routes.url_helpers
end
class ActiveSupport::TestCase
ActiveRecord::Migration.check_pending!
fixtures :all
end
class ActionDispatch::IntegrationTest
# Make the Capybara DSL available in all integration tests
include Capybara::DSL
include Rails.application.routes.url_helpers
def sign_in(user = nil)
if user.nil?
user = FactoryGirl.create(:user)
$current_user = user
end
login_as(user, :scope => :user)
user.confirmed_at = Time.now
user.save
end
end
So I've been struggling with this for quite some time now and I can't seem to figure our what's going wrong, and couldn't find much on what could possibly cause this issue.
I'm relatively new to Ruby and Rails, as well as test/behavior driven development and am trying to write some acceptance (browser) tests using PhantomJS through Poltergeist, using Rspec and Capybara. I believe some people also call this integration tests (they may be from some perspective), but that's a whole other discussion.
I have a really simple feature that I can't get to do what I want:
require 'feature_helper'
feature 'Logging in', :js => true do
scenario 'with incorrect credentials' do
visit '/login'
puts page.html
save_and_open_page
page.driver.render('_screenshot.png', :full => true)
page.html.should have_selector("title", :text => "hi")
end
end
So. Simple, right. It should just go to /login and throw the HTML content at me, as well I want to see the page using save_and_open_page, and I want it to take a screenshot. I added a simple should have_selector in order to have the test fail in an attempt to get more feedback.
The relative contents of my feature_helper.rb:
require 'spec_helper'
require 'capybara/rspec'
require 'capybara/rails'
require 'capybara/poltergeist'
include Capybara::DSL
Capybara.register_driver :poltergeist do |app|
Capybara::Poltergeist::Driver.new(app, {
:debug => true,
:inspector => true
})
end
Capybara.default_driver = :poltergeist
Capybara.javascript_driver = :poltergeist
FakeWeb.allow_net_connect = %r[^https?://(127.0.0.1|localhost)] # allow phantomjs/poltergeist requests
DatabaseCleaner.strategy = :truncation
RSpec.configure do |config|
config.before :each do
# Set the hostname to something with test
#host = "test.iome:3003"
host! #host
Capybara.default_host = Capybara.app_host = "http://#{#host}/"
Capybara.server_port = 3003
Capybara.reset_sessions!
# Start the database cleaner
config.use_transactional_fixtures = false
DatabaseCleaner.start
end
config.after :each do
DatabaseCleaner.clean
end
end
And also my spec_helper.rb:
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'pry'
require 'fakeweb'
FakeWeb.allow_net_connect = false
It's all fairly simple.
Now, in my console I see the following:
{"name"=>"visit", "args"=>["http://test.iome:3003//login"]}
{"response"=>{"status"=>"fail"}}
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
<html><head></head><body></body></html>
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
{"name"=>"render", "args"=>["_screenshot.png", true]}
{"response"=>true}
{"name"=>"body", "args"=>[]}
{"response"=>"<html><head></head><body></body></html>"}
Also, the screenshot is just a white and empty page. When I tail my log/test.log file, I don't see that a request is being performed. I've tried changing the method visit to get, and that'll make the request, but won't change any of the results.
I've completely run out of ideas of what this could be and it's rather frustrating :(
Final information then about versions:
rspec 2.10.0
capybara 1.1.4
poltergeist 1.0.3
ruby 1.8.7
rails 3.2.13
Unfortunately we're still at ruby 1.8.7, but are working on bumping that version up. Still, I think this shouldn't influence the tests.
Any help would be greatly appreciated!
So eventually I got help from a colleague, and we managed to fix it. We used the lvh.me domain for this, as any request to that domain will resolve in localhost, allowing you to use subdomains without a problem. You could probably also use hostname.127.0.0.1.xip.io for this.
Our spec_helper.rb now looks like this:
# Use capybara in combination with poltergeist for integration tests
require 'capybara/rails'
require 'capybara/rspec'
require 'capybara/poltergeist'
require 'rack_session_access/capybara'
Capybara.default_driver = :poltergeist
Capybara.always_include_port = true
Capybara.app_host = 'http://application-test.lvh.me' # Any lvh.me domain resolves to localhost
Capybara.default_wait_time = 8 # How long capybara should look for html elements
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = 'spec/vcr_cassettes'
config.hook_into :fakeweb
config.ignore_localhost = true
config.configure_rspec_metadata!
config.ignore_hosts 'codeclimate.com'
end
require 'fakeweb'
FakeWeb.allow_net_connect = false
Because we hooked in VCR to record any requests going out during the first run of the integration tests, all your integration tests, or features, should contain this code:
before(:all) do
FakeWeb.allow_net_connect = true
end
after(:all) do
FakeWeb.allow_net_connect = false
end
If you want to change the subdomain during your specs, you can use the following:
before(:each) do
#original_host = Capybara.app_host
Capybara.app_host = 'http://does-not-exist.lvh.me'
visit '/login'
end
after(:each) do
Capybara.app_host = #original_host
end
Making screenshots can now be done using page.save_screenshot during specs. Hope this helps.
I'm receiving the following error when I try to run my request specs:
POST :: /users/:id/authentications request::successful request#test_0001_Adds an authentication record to a user:
NoMethodError: undefined method `post' for #<#<Class:0x007fa607163028>:0x007fa6070012c0>
test/requests/authentications_test.rb:9:in `block (3 levels) in <main>'
Here's the test itself:
require "minitest_helper"
describe "POST :: /users/:id/authentications request" do
describe "successful request" do
it "Adds an authentication record to a user" do
user = create_user
post user_authentications_path(user)
response.status.must_equal "200"
end
end
end
Here's the minitest_helper.rb file:
ENV["RAILS_ENV"] = "test"
require File.expand_path("../../config/environment", __FILE__)
require "rails/test_help"
require "minitest/autorun"
require "minitest/rails"
require "minitest/rails/capybara"
Dir[Rails.root.join("test/support/**/*.rb")].each {|f| require f}
class ActiveSupport::TestCase
end
# database cleaner
DatabaseCleaner.strategy = :transaction
class MiniTest::Spec
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
class RequestTest < MiniTest::Spec
include Rails.application.routes.url_helpers
register_spec_type(/request$/, self)
end
Relevant versions of things:
Rails: 3.2.13
minitest-rails: 0.9.2
minitest-rails-capybara: 0.9.0
It really doesn't make sense that I can't call post. It looks like every other example out the web can do it just fine.
Any help with this is greatly appreciated.
You have a lot going on in your test helper. It seems that you have copied several different approaches to running minitest in your rails tests. I suggest the following:
Remove the following from your test helper:
# database cleaner
DatabaseCleaner.strategy = :transaction
class MiniTest::Spec
before :each do
DatabaseCleaner.start
end
after :each do
DatabaseCleaner.clean
end
end
class RequestTest < MiniTest::Spec
include Rails.application.routes.url_helpers
register_spec_type(/request$/, self)
end
Add the following to your test helper:
class ActionDispatch::IntegrationTest
# Register "request" tests to be handled by IntegrationTest
register_spec_type(/Request( ?Test)?\z/i, self)
end