-
-
Save fcheung/a7db2482d89e7f35133e to your computer and use it in GitHub Desktop.
Nested transactions for your specs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RSpec::Core::ExampleGroup | |
class Context | |
def initialize(target) | |
@_target = target | |
end | |
def method_missing name, *args, &block | |
@_target.send name, *args, &block | |
end | |
end | |
def self.saved_context= value | |
@saved_context = value | |
end | |
def self.saved_context | |
@saved_context | |
end | |
def self.restore_context(target) | |
if superclass.respond_to? :restore_context | |
superclass.restore_context(target) | |
end | |
if saved_context | |
saved_context.each do |name, value| | |
if value[0] < Numeric | |
target.send :instance_variable_set, '@' + name, value[1] | |
else | |
ordering = {} | |
if value[1].is_a?(Array) | |
value[1].each_with_index {|x, index| ordering[index]=x} | |
records = value[0].find(value[1]).index_by(&:id) | |
target.send :instance_variable_set, '@' + name, value[1].collect {|id| records[id]} | |
else | |
target.send :instance_variable_set, '@' + name, value[0].find(value[1]) | |
end | |
end | |
end | |
end | |
end | |
def self.common_context(&block) | |
before(:all) do | |
context = Context.new(self) | |
start_transaction_or_savepoint | |
self.class.saved_context = {} | |
# a context might depend on an outer common context: restore it to the context object before evaluating the block | |
self.class.restore_context context | |
context.instance_eval(&block) | |
context.instance_values.each do |name, value| | |
unless name == '_target' | |
if value.is_a?(Array) && value.all? {|v| v.is_a?(ActiveRecord::Base)} && value.collect {|v| v.class}.uniq.length == 1 | |
self.class.saved_context[name] = [value.first.class, value.collect {|v| v.id}] | |
elsif value.is_a?(Array) && value.all? {|v| v.is_a?(Numeric)} | |
self.class.saved_context[name] = [value.first.class, value] | |
elsif value.is_a?(Numeric) | |
self.class.saved_context[name] = [value.class, value] | |
elsif !value.is_a?(ActiveRecord::Base) | |
raise "Only active record subclasses can be used in common_context, found #{value}" | |
else | |
self.class.saved_context[name] = [value.class, value.id] | |
end | |
end | |
end | |
end | |
before(:each) do | |
self.class.restore_context(self) | |
end | |
after(:all) do | |
rollback_transaction_or_savepoint | |
self.class.saved_context = nil | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
RSpec.configure do |config| | |
config.use_transactional_fixtures = false | |
def start_transaction_or_savepoint | |
ActiveRecord::Base.connection.transaction_joinable = false | |
if ActiveRecord::Base.connection.open_transactions == 0 | |
ActiveRecord::Base.connection.begin_db_transaction | |
else | |
ActiveRecord::Base.connection.create_savepoint | |
end | |
ActiveRecord::Base.connection.increment_open_transactions | |
end | |
def rollback_transaction_or_savepoint | |
ActiveRecord::Base.connection.decrement_open_transactions | |
if ActiveRecord::Base.connection.open_transactions == 0 | |
ActiveRecord::Base.connection.rollback_db_transaction | |
else | |
ActiveRecord::Base.connection.rollback_to_savepoint | |
end | |
end | |
config.around(:each) do |example| | |
start_transaction_or_savepoint | |
begin | |
example.run | |
ensure | |
rollback_transaction_or_savepoint | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Allows a set of activerecord objects to be re-used across specs | |
describe 'Foo' do | |
common_context do | |
Factory.create(:something) | |
end | |
it 'should bar' do | |
Something.count.should == 1 | |
end | |
it 'should baz' do | |
end | |
end | |
things saved to a instance variable are preserved: | |
describe 'Foo' do | |
common_context do | |
@something = Factory.create(:something) | |
end | |
it 'should bar' do | |
@something.bar.should be_true | |
end | |
end | |
They are loaded from the database between specs so that unsaved changes are thrwon away |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment