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.
Related
I am using Machinist to create my test users which I want to log in before each test which I am running with Capybara:
include Capybara::DSL
include ActionController::UrlWriter
before do
SslRequirement.disable_ssl_check = true
user = User.make
visit new_user_session_path
fill_in('username', with: user.email)
fill_in('password', with: '12345')
click_button('submit')
end
it "responds as expected" do
# ...
end
Now when the user is created with User.make I can access it through User.all when it fact it's not persisted in the database yet, this is because
ActiveRecord::Base.connection.open_transactions == 1
I can fix/hack this by doing a
ActiveRecord::Base.connection.commit_db_transaction
after User.make, but I'd rather fix this properly. Is this a Capybara configuration which I am missing? Disclaimer: Using Rails 2.3 and RSpec 1.3
You don't show which driver you're using with Capybara, but generally you shouldn't be using transactions for testing when using Capybara (yes there are potential workarounds to allow it but they all have side-effects). See transactions and database setup and then look into the database_cleaner gem with truncation - here's a blog post about it that should be good with the age of gems you're using - http://devblog.avdi.org/2012/08/31/configuring-database_cleaner-with-rails-rspec-capybara-and-selenium/
I'd like to put a few integration tests in a separate directory from my controller unit specs. However, when I move my spec file to spec/integration, it fails with:
ArgumentError:
bad argument(expected URI object or URI string)
The spec passes correctly when in the spec/controllers directory.
Here's a bit from my spec:
require 'spec_helper'
describe Users::LoginsController, type: :controller do
let!(:user) { User.create(email: 'test#test.com', password: 'test')
it 'logs in the user' do
post :create, email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
end
I'm using Rails 3.1.3, RSpec 2.7.0.
Are there any tricks I have to use to achieve this?
Try specifying type:
describe ProductsController, type: :controller do
it 'can test #show' do
get :show
end
end
Works in Rails 3.2.11
You have to do the following:
describe Users::LoginsController do
controller_name 'users/logins'
... the rest of your spec here ...
end
I am not entirely certain about the nested syntax, but at least you need to specify the controller_name to get it to work.
Hope this helps.
The test framework does not like it if you specify the test action using a symbol.
it 'logs in the user' do
post :create, email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
becomes
it 'logs in the user' do
post 'create', email: 'test#test.com', password: 'test'
controller.current_user.should == user
end
I had the same problem and ended up simply using the URL
get "/users"
It's not as clean but gets the job done. I couldn't get the other suggestions to work.
This approach works for me in Rails 4 and RSpec 2.14.7:
My findings:
Don't name your directory integration. I need to grep through the source of either RSpec or Rails, but it appears to have an adverse effect on running controller specs. Perhaps someone with this knowledge can chime in to confirm this.
Name it something other than integration; in my case I tried int, which resulted in problems until I added the "type: :controller" after the #describe method.
After that, I was able to move all of my slow Rails specs into my int directory, allowing me to create a unit directory for all of my decoupled, fast specs.
Please let me know if this works for you.
By the way, I am running:
Ruby 1.9.3
Rails 4.0.2
rspec-core 2.14.7
rspec-rails 2.14.1
all on Mac OS X.
Take the opportunity now to get rid of your controller and integration specs. They're needlessly painful to write and too coupled to implementation. Replace them with Cucumber stories. Your life will be much easier...
I have all my capybara tests working with my authlogic members area using the default driver, but when i change one test to use selenium driver as it has ajax in it, it gives my theis error :
You must activate the Authlogic::Session::Base.controller with a controller object before creating objects
Things are working with default driver for authlogic so must be something to do with selenium ??
I have include Authlogic::TestCase in my spec_helper and
activate_authlogic
domain.user_sessions.create(user)
in a before each.
Any one help me with this please ?
thanks rick
I posted a cucumber solution here: Log-in through authlogic without having to fill in form every time
For RSpec integration tests it's similar.
In your spec_helper.rb:
require "authlogic/test_case"
RSpec.configure do |config|
...
config.include Authlogic::TestCase
ApplicationController.skip_before_filter :activate_authlogic
config.before(:each, :type => :request) do
activate_authlogic
UserSession.create(User.find_by_email!(email))
end
...
end
Obviously, if your site is not login only you may want to move the two lines in config.before into a before block in your specific test for logged in specs. If you leave as is you can delete the session with UserSession.find.destroy or obviously follow the logout link (if this makes more sense in your spec).
I think the following code will work to activate authlogic:
Authlogic::Session::Base.controller = Authlogic::ControllerAdapters::RailsAdapter.new(self)
Having said that, I prefer defining a step that actually goes to the login form, fills it out, and logs in. It's slower, but I rarely run my entire integration test suite manually, usually the continuous integration server takes care of that.
This work for me (Rails 3.2.1) :
In spec_helper.rb
require 'authlogic/test_case'
include Authlogic::TestCase
In In my controller_specs :
def valid_session
activate_authlogic # run before tests are executed
user = Factory(:user)
UserSession.create(user, true) #create an authlogic session
#user = #controller.current_user
{}
end
# exemple of valid_session utilization in your test:
# valid_session
# user_id = #user.id
#
# or
#
# get :index, {}, valid_session
Enjoy!
When I visit my sign in page in a browser everything works fine.
When I visit my sign in page in an rspec integration/request test, I get the following error:
ActionView::Template::Error:
undefined method `title' for #<#<Class:0x00000007af9180>:0x00000007af32a8>
The title method is used by the view and defined in ApplicationHelper which devise seems to find when using the browser. However, during rspec integration tests, devise is unable to find the helper method.
Is there anything I should be stubbing? It seems wrong to be stubbing in integration tests. Any other ideas?
(This question is not about how to include devise helpers in integration tests. I'm manually filling in the sign in forms to authenticate).
Looks like this issue. (in some cases related to ActiveAdmin https://github.com/gregbell/active_admin/wiki/Use-spork)
Here I found a hack that works for me (REE 1.8.7, Rails 3.1, Capybara, Devise, active_admin).
However, this is not likely to be merged, so I forked spork-rails to here with that patch applied. And as you probably know I can point my Gemfile to that repo:
gem 'spork-rails', :git => "git://github.com/chopmo/spork-rails.git"
Not optimal but it gets the job done for now.
I had a similar problem using Cucumber when I installed devise:
undefined local variable or method `flash_block' for #<#<Class:0x007ffd0a28dae8>:0x007ffd0b2f6d58> (ActionView::Template::Error)
I solved it by including the module in env.rb
Spork.prefork do
include FlashBlockHelper
I hope this helps.
Inside /spec/support create devise.rb with this:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
Make sure your spec_helper.rb includes:
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
and that your specs have:
require 'spec_helper'
This has been causing some frustration recently...
It seems that using Factories in my cucumber tests, in some situations causes AssociationTypeMismatch errors such as:
MyModel(#65776650) expected, got MyModel(#28190030) (ActiveRecord::AssociationTypeMismatch)
These seem to happen when there is an association reference - as if the Factory created object is different to the real one. See this question for more details: Cucumber duplicate class problem: AssociationTypeMismatch
I have been gradually changing Factory calls to real Model.create or mock_model calls. It would be nice to keep using Factory girl... I wonder if there is something I may have done wrong?
Thank you
I had this happening with me on Rails 3.1.0 rc5, and got it working.
To expand on Jonas' answer.
You should change your Gemfile to be like this:
gem 'factory_girl', '~> 2.0.0', :require => false
gem 'factory_girl_rails', '~> 1.1.0', :require => false
And then if you are using Spork, make your spec/spec_helper.rb file look like this:
Spork.each_run do
require 'factory_girl'
require 'factory_girl_rails'
end
It seems to happen if ActiveSupport unloads and reloads a constant that you have a reference to.
I've experienced the same with Rspec/Capybara, and what helped was a mixture of different things:
Make sure you have cached_classes set to false in your test environment (config/environments/test.rb)
In your gemspec, try replacing require 'factory_girl_rails' with 'factory_girl'
I'm using Spork (a test server), which seems to make this stuff increasingly difficult.
If you are using a test server, evaluate whether you should put ', :require => false' after factory_girl in your gemspec.
The topic is also covered in this google groups thread
Please let us know if any of this helped.
If you're using Spork, make sure to reload your factories after reloading your models.
E.g.
Spork.each_run
if Spork.using_spork?
print "Reloading models ... "
ActiveSupport::Dependencies.clear
puts "done"
print "Reloading factories ... "
FactoryGirl.reload
puts "done"
end
end
This happens because cache_classes is false, as is required by Spork. Capybara reloads Rails classes for every request (or, to be correct, Rails' reloader middleware does, which is not called for normal tests), and this freaks out the factories (exactly why, I'm not sure). You can either reload them, or simply run your Capybara specs outside of Spork.
So you need two things: to only run Capybara outside of Spork, and to set cache_classes to false only for Spork.
To only run Capybara outside of Spork, I have a Guardfile that runs specs in spec/requests outside of Spork and other specs inside of Spork here:
https://gist.github.com/1731900
Then, in config/environments/test.rb:
config.cache_classes = !ENV['DRB']
Your Capybara specs will be a bit slower, as they need to boot rails, but everything will Just Work.
I had some success with reloading the Factory definitions try something like this:
class Factory
def self.reload_definitions #:nodoc:
self.factories.clear
definition_file_paths.each do |path|
load("#{path}.rb") if File.exists?("#{path}.rb")
if File.directory? path
Dir[File.join(path, '*.rb')].each do |file|
load file
end
end
end
end
end
I ran into this issue when I passed the "class" option to my factory that was inherited by other factories:
factory :draft_resource, :class => Resource do
factory :resource, :parent => :draft_resource do
The only solution I could find was to simply not do this.
I ran into this same issue and spent probably ten hours trying every solution on this thread and everywhere else on the web. I started ripping out huge chunks of code trying to get it as close to another app of mine in which I couldn't reproduce the problem. Finally, I came across some helper functions in my spec_helper file:
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
# Sign in when not using Capybara as well.
cookies[:remember_token] = user.remember_token if defined?(cookies)
end
A sign_in helper intended to work both in controller and request specs. And it does, sort of--just not with spork. When I removed the capybara helpers the issue was resolved:
def sign_in(user)
cookies[:remember_token] = user.remember_token
end