Skip to content

Instantly share code, notes, and snippets.

@hernan
Forked from axehomeyg/sqlite_test_db_loader.rb
Created February 24, 2022 02:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hernan/f384a2ad52d9544faa39fe5f340d491a to your computer and use it in GitHub Desktop.
Save hernan/f384a2ad52d9544faa39fe5f340d491a to your computer and use it in GitHub Desktop.
Rails 6 parallel minitest with sqlite3 :memory: databases.
#### evals the schema into the current process
class SqliteTestDbLoader
# assumes your schema is generated for MySQL
# tweak for Postgres!
MYSQL_REPLACEMENTS = {
/ENGINE=InnoDB DEFAULT CHARSET=[a-z0-9]*/ => '',
/, collation: "[^"]*"/ => ''
}
class << self
def reload!(context)
new.reload!
ensure
# puts "Reloaded Schema: #{context}"
end
end
# grab schema from fs
def extract
File.read(
Rails.root.join(
"db", "schema.rb"))
end
# Get rid of non-standard SQL unimportables
def transform(schema)
MYSQL_REPLACEMENTS
.reduce(schema) do |res, replacement|
res.gsub(*replacement)
end
end
# load into sqlite's :memory: db
def load(schema)
eval(schema)
end
def reload! ; load(transform(extract)) ; end
end
# Always load schema into :memory: after a new connection
module SqlitePostConnectionLoad
def establish_connection(*args, &block)
super
connection
.migration_context
.needs_migration? &&
SqliteTestDbLoader.reload!("#{self.class.name}#establish_connection")
end
end
# "rails/test_help" needs a connection early on in the test_helper load process
module CheckPendingSoftly
def check_pending!(conn = ActiveRecord::Base.connection)
conn
.migration_context
.needs_migration?
.yield_self do |needs_migration|
needs_migration &&
SqliteTestDbLoader.reload!("#{self.class.name}#check_pending!")
end
end
end
module PreserveParallelMemoryDatabase
def create_and_load_schema(i, env_name:)
if ActiveRecord::Base
.configurations
.configs_for(env_name: env_name)
.to_s.match(/:memory:/m)
puts "Memory database found. Skipping db creation"
ActiveRecord::Base.establish_connection(Rails.env.to_sym)
return
end
super
end
end
ActiveSupport.on_load(:active_record) do
ActiveRecord::Migration.singleton_class.prepend CheckPendingSoftly
ActiveRecord::TestDatabases.singleton_class.prepend PreserveParallelMemoryDatabase
end
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
# must load BEFORE rails/test_help
require_relative './support/sqlite_test_db_loader.rb'
require 'rails/test_help'
require "minitest/rails"
# This assumes you're sharing config between unit/integration
module TestSetup
extend ActiveSupport::Concern
included do
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
fixtures :all
# Use Jest-like CI flag
parallelize(workers: ENV['CI'] ? 1 : :number_of_processors, with: :processes)
# Make sure that you reload the sqlite when starting processes
parallelize_setup do
# slightly more efficient than a direct call to establish_connection
ActiveRecord::Migration.check_pending!
end
end
end
class ActiveSupport::TestCase
include TestSetup
end
class ActionDispatch::IntegrationTest
include TestSetup
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment