Rails action caching RSpec test fails - ruby-on-rails

I have a spec that tests action caching when caching is disabled and when caching is enabled. It seems like the order of test execution affects whether they pass or not.
it "should not cache the index page when we're not caching" do
ActionController::Base.perform_caching = false
HomeController.caches_action :index
Rails.cache.clear
ActionController::Base.cache_store.exist?(:index_cache_path).should be_false
get :index
ActionController::Base.cache_store.exist?(:index_cache_path).should be_false
end
it "should cache the index page when we're caching" do
ActionController::Base.perform_caching = true
HomeController.caches_action :index
Rails.cache.clear
ActionController::Base.cache_store.exist?(:index_cache_path).should be_false
get :index
ActionController::Base.cache_store.exist?(:index_cache_path).should be_true
end
When tests are run in the above order the last test fails because the cache_store does not exist in the last expectation. I'm stumped on why the no caching test is affecting the caching test. Does anyone know what is wrong?

If you have spec_helper.rb random test order turned to true, it makes sense since you're not un-doing your "ActionController::Base.perform_caching = false" setting.
The recommended way to write caching tests is to do a before(:each) and after(:each) setting the caching settings on and off.
Since you're testing these settings, if you turn it on, remember to turn it off before the end of the test, and vice-versa. Your tests will be more atomic.

Make sure that you have:
config.action_controller.perform_caching = true
In environment/test.rb.
Else - i noticed super strange thing. Caching worked when I run only requests tests (spring rspec spec/requests describe '..' type: :request), but same tests failed if I run everything with rspec spec.

Related

Specs of my Rails app fail randomly because of wrong default locale

From time to time, some specs of my Rails app seem to fail randomly because suddenly English is not the default language anymore, but German:
expected: "Project test customer - Project test name (Audit, Access for all, 2015-06-15).pdf"
got: "Project test customer - Project test name (Audit, Zugang für alle, 2015-06-15).pdf"
As one can see, the part "Access for all" suddenly is "Zugang für alle". I googled for a solution and it seems that I18n.locale is a global object, so when it's changed in a spec, it persists.
The problem doesn't always occur, but I can reproduce it when specifying a seed like this: rspec --seed 51012. So it really seems to be some issue with a spec being executed before (or after) some other spec.
I have a feature spec which tests whether the locale can be changed, like this:
it 'offers contents in german' do
visit root_path(locale: :de)
expect(page).to have_content 'Willkommen'
end
I suspect this could be the problematic spec, and when it's run early, it has impact on other specs.
I hoped that I could solve it by setting the language in the spec back to the default like this:
it 'offers contents in german' do
visit root_path(locale: :de)
expect(page).to have_content 'Willkommen'
I18n.locale = :en
end
Didn't work, also this one didn't:
it 'offers contents in german' do
visit root_path(locale: :de)
expect(page).to have_content 'Willkommen'
visit root_path(locale: :en)
end
I'm a bit clueless now. How could I debug the situation, so I definitely find the source of the problem and fix it (or at least work around it)?
Update
Using Dave's answer (rspec --bisect) I found the problem.
# application_controller_spec.rb
describe ApplicationController do
controller(ApplicationController) do
def index
render text: 'Hello World'
end
end
describe 'locale parameter' do
it 'is set to english when not available in the request' do
get :index
expect(I18n.locale).to eq :en
end
it 'can be set through the request' do
get :index, locale: :de
expect(I18n.locale).to eq :de
end
end
end
Depending on the order that these specs were run, the locale was set to :de or :en for following specs.
I fixed it with BoraMa's suggested code snippet:
RSpec.configure do |config|
config.after(:each) { I18n.locale = :en }
end
I'm still a bit astonished that RSpec/Rails don't do this on their own automatically...
Comment out the spec that you suspect and run rspec --seed 51012. If that passes, you know the culprit.
If not, this looks like a job for rspec --bisect. Do
rspec --seed 51012 --bisect
and let RSpec find the minimal set of examples (probably 2) that reproduce the failure. You can then decide what to do about the culprit.
This feature is available in RSpec 3.3 and better in RSpec 3.4. More here: https://relishapp.com/rspec/rspec-core/docs/command-line/bisect

RSpec leaves record in test database

Whenever I run a user test, RSpec leaves the Fabricated user in the test database after the test has completed, which is messing up my other tests. I will do a rake db:test:prepare, but when I run my tests again, the record is recreated in my database. I have no idea why this is happening. It only happens with user objects.
In my spec_helper file I even have:
config.use_transactional_fixtures = true
Here is an example test that creates a record:
it "creates a password reset token for the user" do
alice = Fabricate(:user)
post :create, email: alice.email
expect(assigns(alice.password_reset_token)).to_not eq(nil)
end
Fabricator:
Fabricator(:user) do
email { Faker::Internet.email }
password 'password'
name { Faker::Name.name }
end
Could this have anything to do with my users model?
you should use a gem called database_cleaner that will truncate your database and reset everything automatically so in your gem file add the gem database_cleaner after that inside your spec_helper.rb configure it
spec_helper.rb
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
and then create a new file in your spec/support directory
spec/support/shared_db_connection.rb
class ActiveRecord::Base
mattr_accessor :shared_connection
##shared_connection = nil
def self.connection
##shared_connection || retrieve_connection
end
end
ActiveRecord::Base.shared_connection=ActiveRecord::Base.connection
Now whenever you run your tests the database will be reset.This was taken from the book 'Everyday Rails testing with RSpec' by Aaron Sumner
Each test is wrapped in a database transaction. That means that everything created during the test should be gone when the test finishes. Therefore, I would suspect that whatever you have in your database was made outside the test itself (like in a before(:all) block).
Also this doesn't guarantee that your database will be empty each time you run your tests. It might be possible that you accidentally added a record somehow, and now it just keeps reverting to that state.
If you want to make sure your tests have a shiny database each time, you should have a look at the database_cleaner gem.
The simplest solution is to make sure RSpec tests run in transactions (Rails does this by default)
spec_helper.rb
config.around(:each) do |example|
ActiveRecord::Base.transaction do
example.run
raise ActiveRecord::Rollback
end
end
If I had to guess, the line post :create, email: alice.email seems like a likely candidate for doing the actual user creation.
Stub that line out with an bogus test and see if you're still getting a user created in the DB.
My problem turned out to be that I was was using before(:all) in my RSpec files which I found out the hard way that the data created does not get rolled back. I switched to before(:example) per this article: https://relishapp.com/rspec/rspec-rails/docs/transactions

Why are these Rspec examples not occuring within a DB transaction?

I'm writing a typical test in my application where I create a model through a form and check that the model count equals 1.
The test fails because there are already multiple records in the test DB, and this count increases each time I run my tests. It looks like each example isn't happening inside a transaction (being rolled back) like it's supposed to, and I don't know why.
I have this line in my spec_helper.rb file, which is supposed to run each example in a transaction:
config.use_transactional_fixtures = true
Here is my spec that keeps generating model objects:
require 'spec_helper'
describe "Admin artwork pages" do
subject { page }
let(:gallery) { FactoryGirl.create(:gallery) }
describe "artwork creation" do
context "with valid attributes" do
it "creates new artwork" do
visit admin_gallery_artworks_path(gallery_id: gallery.id)
click_link 'Add new artwork'
fill_in 'artwork_title', with: 'Still Life'
click_button 'Create Artwork'
page.should have_text 'Successfully created'
Artwork.count.should eq 1
end
end
end
end
Here's the error message from Rspec:
Failures:
1) Admin artwork pages artwork creation with valid attributes creates new artwork
Failure/Error: Artwork.count.should eq 1
expected: 1
got: 153
(compared using ==)
Edit: Contents of my spec_helper.rb file:
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'
require 'capybara/rails'
require 'capybara/rspec'
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
RSpec.configure do |config|
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = true
# If true, the base class of anonymous controllers will be inferred
# automatically. This will be the default behavior in future versions of
# rspec-rails.
config.infer_base_class_for_anonymous_controllers = false
# Run specs in random order to surface order dependencies. If you find an
# order dependency and want to debug it, you can fix the order by providing
# the seed, which is printed after each run.
# --seed 1234
config.order = "random"
# Include route helpers
config.include Rails.application.routes.url_helpers
#
# Take the FactoryGirl out of FactoryGirl.create
config.include FactoryGirl::Syntax::Methods
end
I'm using Rails 4.0.0.rc1, Ruby 1.9.3, FactoryGirl and rspec-rails 2.13.0 Thanks for any help.
It turns out that Rails 4 is supported starting in rspec-rails 2.13.1 - I was using 2.13.0. After upgrading, the specs took place within a transaction like they were supposed to.
Thanks to everyone who took the time to post help.
I believe the problem is the way you have your test written and less to due with config.use_transactional_fixtures = true. Focus on the bottom of the error that says (compared using ==)
Try to use the expecting change rspec syntax instead
Change this:
click_button 'Create Artwork'
page.should have_text 'Successfully created'
Artwork.count.should eq 1
To this:
expect { click_button 'Create Artwork' }.to change { Artwork, :count }.by(1)
page.should have_text 'Successfully created'
Let me know if this helps
You're running a request spec: when you call visit the code under test is run in a server instance (in the same process). In particular this means that it's using a different thread.
As a result the application code ends up using a different database connection, and since transactions are a per connection thing there is no transaction used when your controller inserts records into the database.
There are several ways to address this. One is to abandon rspec's transactional fixtures and use the database_cleaner gem. You can set it up so that controller and model specs use transactions but request specs use truncate to forcibly clear out tables.
Another approach is to try and force both the spec code and the server code to use the same database connection, this eliminating the problem. You can see this approach in this answer. In my experience this works pretty well until you start using a capybara driver such as poltergeist which will run any javascript on the page and your page fires ajax requests.
The approach I've been using is to set the active record connection pool size to 1: there is only 1 connection allowed so everyone will use the same one. You do then have to do some work to ensure that connections are returned to the pool or your spec just hangs.
I wrote up the details a while ago as a blog post, but in a nutshell you need to
call ActiveRecord::Base.clear_active_connections! before calling methods like visit, click and so on
hack config.middleware.insert_before ActiveRecord::ConnectionAdapters::ConnectionManagement so that it clears the connection after each request (by default it doesn't do this in tests).

Starting rails before rspec tests?

Is there any way within an RSpec tests, by convention or code, to have rails start before tests run? I'm trying to setup a testing framework for selenium tests that use chrome, and now I'm only hindered by my lack of a running server.
require 'spec_helper'
describe 'The first tab' do
before(:each) do
#driver = Selenium::WebDriver.for :chrome
end
it 'Shows the list' do
#driver.navigate.to 'index.html'
end
end
I'm new to RSpec, so I'm not sure how I would create a suite of tests that all ran while a rails server was running.
You should be using Capybara to test this stuff instead. It uses selenium-webdriver internally to provide JavaScript testing support.
with Capybara, you put this test in either the spec/integration or spec/requests folder and write it like this:
require 'spec_helper'
describe 'The first tab' do
it "shows the list", :js => true do
visit list_path
end
end
By putting :js => true after the example's name Capybara will know to run this as a JavaScript test.
Just run rails server and kill the process went tests complete.

authlogic not working with capybara when using the selenium driver

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!

Resources