Skip to content

Instantly share code, notes, and snippets.

@axehomeyg
Created December 9, 2019 20:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save axehomeyg/55236a3b7037222504ed7e9a7aaa0321 to your computer and use it in GitHub Desktop.
Save axehomeyg/55236a3b7037222504ed7e9a7aaa0321 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
@joel
Copy link

joel commented Mar 17, 2021

rails test call behind the scenes those rake tasks:

  • rake task db:load_config
  • rake task db:check_protected_environments
  • rake task db:test:purge
  • rake task db:test:load
  • rake task db:test:load_schema

As db:test:load_schema load the schema.rb before modification, see here, it will fail.

You can either suppress the load, like this.

# frozen_string_literal: true

Rake::Task["db:test:load_schema"].clear

namespace :db do
  namespace :test do
    task :load_schema do
      puts("OVERRIDDEN rake task db:test:load_schema")
    end
  end
end

Or prior change the schema.rb into memory_suitable_schema.rb and set ENV["SCHEMA"] = memory_suitable_schema.rb, see here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment