Turn off transactional fixtures for one spec with RSpec 2
Asked Answered
R

9

44

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?

Rupertruperta answered 4/10, 2010 at 5:35 Comment(3)
it's really wired to want that. because you can't know what you have in your database.Haiku
I hope that's not true for your code, too ;-) But seriously, for some testing scenarios you have to disable the transactional fixtures. For example, like in my case, to test Thinking Sphinx. Sphinx needs to update the search index from the "outside". And so it has to know the database content at a specific time.Rupertruperta
Just one thing to note; At least in my environment ( Rails4 rspec 2 ) if you for some reason include the rspec/rails_helper in any of your rspec files, it will cause any test after it in the suite or in subsequent files to be run transactionally again.Vowel
J
9

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

Joiner answered 13/10, 2010 at 0:25 Comment(3)
Thanks for pointing me to the ticket, Lailson. Answer accepted :-) I also use a workaround as I don't like to disable transactional fixtures globally (too slow). I simply do a MyClass.connection.commit_db_transaction() after the objects were created. Then I do the test and afterwards delete the database entries. That works fine, and all other tests still use the transactional feature.Rupertruperta
Nice. As I said, you can do this cleanly with DatabaseCleaner (see the github repo README I pointed to know how to do this). But your solution is viable too, just a bit error-prone. But if it's working and you're satisfied, cool… =)Joiner
I tried MyClass.connection.commit_db_transaction but it left the connection in an odd state where it thought it had an open transaction but it did not. This made certain active record commands not work and also led to warnings. This seemed to fix that problem, actually closing the transaction in the db and updating the connection to know it no longer had an open transaction: MyClass.connection..transaction_manager.commit_transactionAlto
S
37

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
Spandau answered 9/10, 2011 at 11:37 Comment(3)
are you sure it works? i've tried here to test something that was using rails transactions and not worked :(Halfpint
You can just change the DatabaseCleaner.strategy in a before/after block of your specs too.Leandra
Don't we have to set the self.use_transactional_fixtures to true again? in after(:all) block? I guess it will set as false for other specs which will run.Vani
H
10

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
Halfpint answered 18/5, 2012 at 15:30 Comment(0)
S
10

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
Shaman answered 8/2, 2019 at 20:58 Comment(0)
J
9

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

Joiner answered 13/10, 2010 at 0:25 Comment(3)
Thanks for pointing me to the ticket, Lailson. Answer accepted :-) I also use a workaround as I don't like to disable transactional fixtures globally (too slow). I simply do a MyClass.connection.commit_db_transaction() after the objects were created. Then I do the test and afterwards delete the database entries. That works fine, and all other tests still use the transactional feature.Rupertruperta
Nice. As I said, you can do this cleanly with DatabaseCleaner (see the github repo README I pointed to know how to do this). But your solution is viable too, just a bit error-prone. But if it's working and you're satisfied, cool… =)Joiner
I tried MyClass.connection.commit_db_transaction but it left the connection in an odd state where it thought it had an open transaction but it did not. This made certain active record commands not work and also led to warnings. This seemed to fix that problem, actually closing the transaction in the db and updating the connection to know it no longer had an open transaction: MyClass.connection..transaction_manager.commit_transactionAlto
G
6

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.

Gerdy answered 12/11, 2017 at 8:13 Comment(0)
V
3

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
Vani answered 25/3, 2019 at 14:55 Comment(0)
R
2

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

Rahmann answered 17/1, 2023 at 10:55 Comment(1)
uses_transaction should be used with caution, as per this discussion in rspec_railsgithub.com/rspec/rspec-rails/issues/…Shrug
N
1

I needed to switch off transactional fixtures for only one test in the suite. Nothing written above worked for me (Rails 6.1.7.6).

I had to write the test setup like that

describe '.method', use_transactional_fixtures: false, truncate: true do
end

And in the rails helper inside Rspec.config block, I added following code:

  config.before do |example|
    config.use_transactional_fixtures = (example.metadata[:use_transactional_fixtures] || true)

    DatabaseCleaner.strategy = if example.metadata[:truncate]
                                 :truncation
                               else
                                 :transaction
                               end
    DatabaseCleaner.start
  end

  config.after do
    config.use_transactional_fixtures = true
  end

  config.append_after do
    DatabaseCleaner.clean
  end

This allowed me keeping transactional fixtures for all other tests in the suite

Nonobservance answered 18/1 at 16:49 Comment(0)
F
0

Thanks for Maxence's answer, I found very practical hack.

You can write this magical phrase def self.uses_transaction?(_method) = true in any describe/context block.

For example:

describe "transactional test" do # or context
  def self.uses_transaction?(_method) = true

  # any testcase in this block is out of transaction
end
Faxen answered 1/2 at 2:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.