Skip to content

Instantly share code, notes, and snippets.

@alexcastrodev
Last active July 28, 2024 12:08
Show Gist options
  • Save alexcastrodev/5657332d6d8a32a179a51b9dca3b7330 to your computer and use it in GitHub Desktop.
Save alexcastrodev/5657332d6d8a32a179a51b9dca3b7330 to your computer and use it in GitHub Desktop.
parallel rake rspec
# frozen_string_literal: true
namespace :tests do
desc "Create multiple test databases, load schema, seed data, and run RSpec tests in parallel"
task :parallel, [:quantity] => :environment do |t, args|
quantity = args[:quantity].to_i
raise ArgumentError, "Please provide a valid quantity greater than 0." if quantity <= 0
test_config = ActiveRecord::Base.configurations.configurations.find { |config| config.env_name == 'test' }
raise "Test configuration not found." if test_config.nil?
errors = []
db_names = []
quantity.times do |i|
db_name = "test_#{SecureRandom.hex(4)}_#{i}"
db_names << db_name
config = test_config.configuration_hash.deep_dup
config['database'] = db_name
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
ActiveRecord::Base.connection.drop_database(config['database']) rescue nil
ActiveRecord::Base.connection.create_database(config['database'])
ActiveRecord::Base.establish_connection(config)
system("DATABASE_URL=#{get_database_url(config, db_name)} rails db:schema:load --trace")
system("rails db:seed")
rescue ActiveRecord::StatementInvalid => e
errors << e.message
end
end
unless errors.empty?
drop_databases(db_names)
raise errors.join("\n")
end
test_files = Dir.glob("spec/**/*_spec.rb")
files_per_db = (test_files.size / quantity.to_f).ceil
Parallel.each_with_index(db_names, in_threads: quantity) do |db_name, index|
config = test_config.configuration_hash.deep_dup
config['database'] = db_name
files_to_run = test_files.slice(index * files_per_db, files_per_db)
next if files_to_run.nil? || files_to_run.empty?
thread_config = config.dup
thread_config['database'] = db_name
database_url = get_database_url(thread_config, db_name)
puts "Running tests for #{db_name}... in #{database_url}"
ActiveRecord::Base.establish_connection(thread_config)
system("DATABASE_URL=#{database_url} rspec #{files_to_run.join(' ')} --fail-fast --format documentation")
end
drop_databases(db_names)
end
private
def get_database_url(config, db_name = nil)
"#{config[:adapter]}://#{config[:username]}:#{config[:password]}@#{config[:host]}/#{db_name || config[:database]}"
end
def drop_databases(db_names)
test_config = ActiveRecord::Base.configurations.configurations.find { |config| config.env_name == 'test' }
db_names.each do |db_name|
ActiveRecord::Base.establish_connection(test_config.configuration_hash.merge('database' => db_name))
ActiveRecord::Base.connection.drop_database(db_name) rescue nil
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment