How do I turn off transactional fixtures for only one spec (or Steak scenario) with RSpec 2?
I tried some things found on the web without any success.
This leads to an undefined method exception.
describe "MyClass without transactional fixtures" do
self.use_transactional_fixtures = false
...
end
This simply does nothing (transactional fixture is still on):
describe "MyClass without transactional fixtures" do
RSpec.configure do |config|
config.use_transactional_fixtures = false
end
...
end
What else could I try?
I usually add a helper like this:
def without_transactional_fixtures(&block)
self.use_transactional_fixtures = false
before(:all) do
DatabaseCleaner.strategy = :truncation
end
yield
after(:all) do
DatabaseCleaner.strategy = :transaction
end
end
Which lets me turn off transactional fixtures for a specific block in the specs:
describe "doing my thing" do
without_transactional_fixtures do
it "does something without transaction fixtures" do
...
end
end
end
I've did it this way, with database_cleaner, in order to test code that uses transactions (which will conflict with transactional_fixtures or any other strategy to make transactional tests e.g. DatabaseCleaner.strategy = :truncation or :transaction):
# spec_helper.rb
config.use_transactional_fixtures = false
config.around(:each, :testing_transactions => true) do |ex|
DatabaseCleaner.strategy = nil
ex.run
DatabaseCleaner.strategy = :truncation
end
and in my test cases:
it "should not save if one of objects are invalid", :testing_transactions => true
This used to be a bug (see ticket #197), but I seems to be okay now. I just don't know if it will work on a per test base (probably not). If you want to do this, you can disable transactional fixtures globally by putting config.use_transactional_fixtures = false on the spec_helper.rb and use DatabaseCleaner to set that.
I've had a similar problem when testing pages with javascript on the browser (a scenario that does not work with transactional fixtures). Here's how I managed to work around it: http://github.com/lailsonbm/contact_manager_app
I mixed both answers and it worked for me on RSpec 3:
config.around(:each, use_transactional_fixtures: false) do |example|
self.use_transactional_fixtures = false
example.run
self.use_transactional_fixtures = true
DatabaseCleaner.clean_with(:deletion)
end
You can then use it in the describe, context or it block
describe 'my test', use_transactional_fixtures: false do
...
end
Not sure if that applies to RSpec2, but works fine with 3.
config.use_transactional_fixtures = true
config.around(:each, use_transactional_fixtures: false) do |example|
self.use_transactional_tests = false
example.run
self.use_transactional_tests = true
end
Mind the use_transactional_fixtures (rspec-rails option) and use_transactional_tests (activerecord fixtures option) difference.
Use use_transactional_tests instead of use_transactional_fixtures When Rspec 2.3.8 is being used
def without_transactional_fixtures(&block)
self.use_transactional_tests = false
before(:all) do
DatabaseCleaner.strategy = :truncation
end
yield
after(:all) do
DatabaseCleaner.strategy = :transaction
end
self.use_transactional_tests = true
end
The 2023 answer for RSpec 6.0:
uses_transaction "doesn't run in transaction"
it "doesn't run in transaction" do
expect(ActiveRecord::Base.connection.transaction_open?).to eq(false)
end
it "runs in transaction" do
expect(ActiveRecord::Base.connection.transaction_open?).to eq(true)
end
https://github.com/rspec/rspec-rails/blob/v6.0.1/spec/rspec/rails/fixture_support_spec.rb#L21
Related
I have transactions set up in my rails_helper...
config.use_transactional_fixtures = true
My before each blocks look like this:
before(:each) do
allow(subject).to receive_messages(
:authenticate => true,
:authorize => true
)
#user_song = FactoryGirl.create(:user_song)
FactoryGirl.create(:user_playlist, user_id: #user_song.user_id,
album_id: #user_song.song.album_id)
allow_any_instance_of(ApplicationController).to receive_messages(
:current_user => #user_song.user
)
Tests fail with validation errors, but, if I insert a Model.destroy_all for each used model before FactoryGirl's creation, it works. I'm confused.
Why? The Rspec rails guide says that it should be rolled-back? Is it something with FactoryGirl?
config.use_transactional_fixtures = true
is for Rails builtin fixeture cleanup. It wound work with factory_girl. You should disable that and, install a db cleaner gem like this one: https://github.com/DatabaseCleaner/database_cleaner
Try to configure the database cleaner.
Add this to your Gemfile
gem 'database_cleaner'
then update your
spec/rails_helper.rb:
RSpec.configure do |config|
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
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
end
You need to install and setup database cleaner gem. Configured and installed properly, it will fix this problem you're having. There are many blog articles to help with the setup and install if the github read me docs seem confusing. Such as this article
I have following line in my rails helper.
config.use_transactional_fixtures = true
But I want to modify it to
config.use_transactional_fixtures = false
But I don't want to modify it for every tests inside spec folder. I just want to apply it to all the tests inside spec/requests folder.
How can I do it ?
After doing a bit more research, it looks like you need to use the DatabaseCleaner gem https://github.com/DatabaseCleaner/database_cleaner. In your rails helper, you would add this:
require 'database_cleaner'
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before type: :request do
DatabaseCleaner.strategy = :truncation
end
config.after type: :request do
DatabaseCleaner.strategy = :transaction
end
config.before :each do
DatabaseCleaner.start
end
config.after do
DatabaseCleaner.clean
end
end
This will set you up so that request specs will use the truncation strategy (removing all data from the database) and everything but request specs use transactions (rollback all changes from runnning scenario).
I have a JS feature spec I'm trying to run with Capybara Webkit. It doesn't seem to be able to find my database records however.
The spec in question looks like this
it "should allow pledging to a Hardback level", js: true do
book = FactoryGirl.create :book
visit book_path(book)
click_link "pledge-btn"
end
Unfortunately, the request to book_path(book) 404s because the book cannot be found.
If I take the :js flag off, the test passes.
I have DatabaseCleaner set up to use :truncation for JS specs as is the recommended method.
# spec/support/database_cleaner.rb
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
DatabaseMetadata.create!(:sanitized_at => DateTime.now)
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I can puts the book in the test and it will be found.
it "should allow pledging to a Hardback level", js: true do
book = FactoryGirl.create :book
visit book_path(book)
p Book.first
# => .. the book instance
click_link "pledge-btn"
end
I've also tried this shared connection method which doesn't seem to fix the problem either.
What else could be wrong?
You may have config.use_transactional_fixtures = true set in your spec_helper.rb. This would override what you have above.
You want to either remove this line from your spec_helper.rb or change it there to be false.
I ran into this same issue and had a very similar config. After looking through the DatabaseCleaner README, I found this small note:
It's also recommended to use append_after to ensure DatabaseCleaner.clean runs after the after-test cleanup capybara/rspec installs.
Source
That means changing your
config.after(:each) do
DatabaseCleaner.clean
end
to
config.append_after(:each) do
DatabaseCleaner.clean
end
Note the append_after instead of after. This fixed my problem.
Working on legacy project I have had such issue, it was caused by switching DatabaseCleaner strategy to :truncation like the following:
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
Test::Seeder.seed!
DatabaseCleaner.strategy = :transaction
end
so, removing DatabaseCleaner.strategy = :transaction helped in my case
If you create records in a before(:all) block then they will be available.
before :all do
#book = FactoryGirl.create :book
end
it "should allow pledging to a Hardback level", js: true do
visit book_path(#book)
click_link "pledge-btn"
end
Capybara runs the rails server in a separate process from the tests, so they each get their own connection to the database. Therefore, the server does not access to the records created in the transaction for the test.
Because they are not inside a transaction, make sure that you clean them up with DatabaseCleaner in your spec_helper.rb:
config.after(:all, :type => :feature) do
DatabaseCleaner.clean
end
For anyone that lands here in 2019 and beyond, I was caught out by the following code which I copied verbatim from the DatabaseCleaner readme:
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
unless driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
This is all well and good, but I am using Rails system specs, not RSpec features and therefore this code block was never being run.
Change config.before(:each, type: :feature) to config.before(:each, type: :system) if you're using system specs!
I think your main issue is that your rails_helper.rb has the following line commented out:
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
This means your database_cleaner.rb is never getting loaded.
I have a controller sending in a list of vendors to my controller, and on normal view it's working fine.
class VendorsController < ApplicationController
respond_to :html, :json
def index
#vendor_categories = VendorCategory.where(:is_top_level => true)
#vendors = Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC')
respond_with #vendors
end
end
In my view I have the following two lines:
= debug #vendors
= debug current_user.user_vendor_choices
which, again, are working if I view it in the browser. However, if I test it with Capybara and RSpec, it's empty.
require 'spec_helper'
describe 'Vendors' do
before do
category = create(:vendor_category)
5.times do
vendor = create(:vendor)
vendor_categorization = create(:vendor_categorization, vendor: vendor, vendor_category: category)
p vendor
p category
p vendor_categorization
end
visit signup_path
#new_user = sign_up
end
before(:each) do
visit destroy_user_session_path
visit new_user_session_path
sign_in #new_user
visit vendors_path
end
it 'should save selected vendors', js: true do
p Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC').count
end
end
Vendor.all and the above Vendor.includes... both return values, but for some reason in my test it's not showing anything... getting a Capybara::Element not found.
UPDATE
For testing purposes, I created the Vendors directly with the controller:
def index
#vendor_categories = VendorCategory.where(:is_top_level => true)
4.times do
Vendor.create({name: 'Test McTesterson', vendor_tier_id: 1})
end
#vendors = Vendor.includes(:vendor_tier, :vendor_categorizations, :vendor_categories).order('vendor_tier_id DESC, name ASC')
respond_with #vendors
end
Spec passes. What the--? This must be a FactoryGirl issue, or for some reason my records are deleted before it can run the test? Consoling the objects after I create them is showing a record with an ID, which I guess doesn't prove that it's putting them in the database...
Turns out my Database Cleaner activities defined in my spec_helper were a little too vigorous. I had:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
I had to get rid of the second chunk, so it now reads:
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
end
And it works! Not really sure why... any ideas (aside from the obvious, before it was calling database cleaner before/after each test)?
Hi I cursorily glanced at this question, not sure you even need the help anymore, but I think the reason this is failing is a fundamental set up issue that your answer is just patching around.
When you're running a js: true spec (by the way, js: true should be on the describe line, not the it line), short version, Capybara works in different threads, so instance variables created in a before block, unlike with regular Rspec testing, are not available in the spec. To make them available, you have to use a truncation cleaning strategy.
RSpec.configure do |config|
config.use_transactional_fixtures = false
config.before(:each, js: true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
TL;DR when running a js test, truncation is basically required (unless obviously you're running js tests that don't require any database interactions). When running all other tests, use transactions (because it's also much faster). I guess your answer replicated this to some extent =)
I'm having an issue with JS request specs - basic visiting of a model edit page:
it "can edit a doc", :js => true do
doc = FactoryGirl.create(:doc) # tried with Doc.create as well
puts Doc.find(doc.id) # 1 <- so it's definitely in the DB!
visit edit_doc_path(doc)
end
Result: "ActiveRecord::RecordNotFound - Couldn't find doc with id=1"
The odd thing is it works with standard request spec. I tried both webkit and selenium drivers. My spec_helper looks like this (should be pretty standard):
RSpec.configure do |config|
config.use_transactional_fixtures = false
Capybara.javascript_driver = :webkit
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
config.before(:each, :type => :request) do
Capybara.reset_sessions!
end
end
Rails 3.1.4, no versioning for capybara, rspec, etc. test libraries.
Any input much appreciated! Thanks!
Seems like you have same problem as this:
Capybara with :js => true causes test to fail
Try setting DatabaseCleaner strategy to :truncation and see if it works
config.before(:suite) do
DatabaseCleaner.strategy = :truncation
end