Skip to content

Instantly share code, notes, and snippets.

@ahoward
Created February 29, 2012 21:28
Show Gist options
  • Save ahoward/1944528 to your computer and use it in GitHub Desktop.
Save ahoward/1944528 to your computer and use it in GitHub Desktop.
##########################################################################
# something like this which can dump your db in a portable format. you could use mongodump... etc.
#
def App.db_snapshot
reinitialize_loggers if defined?(reinitialize_loggers)
$db_collections = Map.new
Mongoid.master.collections.select do |collection|
name = collection.name
next if name =~ /\bsystem\b/
documents = collection.find().to_a
unless documents.blank?
$db_collections[name] = documents
end
end
$db_collections
end
def App.db_restore(snapshot = $db_collections)
(snapshot || Map.new).each do |name, documents|
collection = Mongoid.master.collections.detect{|collection| collection.name == name}
unless collection.blank? or documents.blank?
collection.drop()
collection.send(:insert_documents, documents, name, check_keys=true, safe=true)
end
end
end
##############################################################################
# a task that dumps a seeded db to yml. this gets called automatically by 'rake test'
##
#
namespace :test do
## kill the default db:test:prepare
#
begin
Rake::Task["db:test:prepare"].tap do |task|
task.instance_eval do
@actions.clear
end
end
rescue Object
end
## build a mo-betta one that runs only when needed
#
task :prepare => :environment do |task, options|
force = !(ENV['FORCE'] || ENV['force']).blank?
test_env do
if force or !prepared?
STDERR.puts
STDERR.puts('db:test:prepare : building test db *** ONCE *** - you can force a rebuild with "rake db:test:prepare:force"')
STDERR.puts
test_env do
`RAILS_ENV=test rake db:bounce 2>&1`
db_snapshot = App.db_snapshot
open(test_db_yml, 'w'){|fd| fd.write(db_snapshot.to_yaml)}
prepared!
end
else
STDERR.puts
STDERR.puts('db:test:prepare : skipping...')
STDERR.puts
end
end
end
namespace :prepare do
task :force do
FileUtils.rm_f(prepared_guard)
Rake::Task['db:test:prepare'].invoke
end
end
private
def test_db_yml
@test_db_yml ||= File.join(Rails.root, 'test/db.yml')
end
def prepared_guard
@prepared_guard ||= File.join(Rails.root, 'test/db.yml')
end
def prepared?
test(?e, prepared_guard)
#ActiveRecord::Base.connection.table_exists?(:db_prepared)
end
def prepared!
FileUtils.touch(prepared_guard)
#ActiveRecord::Base.connection.create_table(:db_prepared) unless prepared?
end
def test_env
Rails.env.tap do |env|
previous = env.to_s
env.replace('test')
return(yield) if block_given?
env.replace(previous)
end
end
end
############################################################
# next, we need to tweek minitest to support a global before filter which will restore the db
##
#
require 'minitest/unit'
require 'minitest/spec'
require 'minitest/autorun'
##
#
MiniTest::Unit::TestCase.i_suck_and_my_tests_are_order_dependent!
## hax in a sane lifecycle DSL
#
class MiniTest::Unit::TestCase
BeforeAll = Hash.new
AfterAll = Hash.new
Before = Hash.new
After = Hash.new
class << self
def before_all(&block)
blocks = (BeforeAll[self] ||= [])
blocks.push(block) if block
ancestors.reverse.each do |ancestor|
blocks = Array(BeforeAll[ancestor])
blocks.each{|block| instance_eval(&block)}
end
self
end
def after_all(&block)
blocks = (AfterAll[self] ||= [])
blocks.push(block) if block
ancestors.reverse.each do |ancestor|
blocks = Array(BeforeAll[ancestor])
blocks.each{|block| instance_eval(&block)}
end
self
end
def before(&block)
blocks = (Before[self] ||= [])
blocks.push(block) if block
ancestors.reverse.map do |ancestor|
blocks = Array(Before[ancestor])
end.flatten.compact
end
alias_method(:setup, :before)
def after(&block)
blocks = (After[self] ||= [])
blocks.push(block) if block
ancestors.reverse.map do |ancestor|
blocks = Array(After[ancestor])
end.flatten.compact
end
alias_method(:teardown, :after)
end
def setup
blocks = self.class.before
blocks.each{|block| instance_eval(&block)}
end
def teardown
blocks = self.class.after
blocks.each{|block| instance_eval(&block)}
end
end
## due to lack of transactions in mongoid we snapshot the db before any test
# is run and then restore to this snapshot before each test *suite* is run. we
# would like to restore before each test but it is simple too slow. the side
# effect is that tests can pollute one another, but test suites cannot. live
# with it.
#
#App.db_snapshot()
#at_exit{ App.db_restore() }
test_db_yml = File.join(Rails.root, 'test/db.yml')
abort('rake db:test:prepare ### did you forget?') unless test(?e, test_db_yml)
TestDb = YAML.load(IO.read(test_db_yml))
class MiniTest::Unit::TestCase
before_all do
App.db_restore(TestDb)
end
end
## tweak the minitest runner to:
#
# 1) snapshot the test db before *any* tests/suites are run
#
# 2) restore to this condition before each suite. this is essentially a
# workaround for the lack of transactions in mongo and the impact of this on
# testing...
#
class MiniTest::Unit
alias_method('___run_suite__', '_run_suite')
def _run_suite(suite, type)
list_of_test_methods = Array(suite.send("#{ type }_methods"))
going_to_run_tests = !list_of_test_methods.empty?
if going_to_run_tests
suite.before_all()
end
___run_suite__(suite, type)
end
end
MiniTest::Unit.runner = MiniTest::Unit.new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment