mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
26821d9b57
Provides both a forked process and threaded parallelization options. To use add `parallelize` to your test suite. Takes a `workers` argument that controls how many times the process is forked. For each process a new database will be created suffixed with the worker number; test-database-0 and test-database-1 respectively. If `ENV["PARALLEL_WORKERS"]` is set the workers argument will be ignored and the environment variable will be used instead. This is useful for CI environments, or other environments where you may need more workers than you do for local testing. If the number of workers is set to `1` or fewer, the tests will not be parallelized. The default parallelization method is to fork processes. If you'd like to use threads instead you can pass `with: :threads` to the `parallelize` method. Note the threaded parallelization does not create multiple database and will not work with system tests at this time. parallelize(workers: 2, with: :threads) The threaded parallelization uses Minitest's parallel exector directly. The processes paralleliztion uses a Ruby Drb server. For parallelization via threads a setup hook and cleanup hook are provided. ``` class ActiveSupport::TestCase parallelize_setup do |worker| # setup databases end parallelize_teardown do |worker| # cleanup database end parallelize(workers: 2) end ``` [Eileen M. Uchitelle, Aaron Patterson]
345 lines
10 KiB
Ruby
345 lines
10 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "isolation/abstract_unit"
|
|
|
|
module ApplicationTests
|
|
class TestTest < ActiveSupport::TestCase
|
|
include ActiveSupport::Testing::Isolation
|
|
|
|
def setup
|
|
@old = ENV["PARALLEL_WORKERS"]
|
|
ENV["PARALLEL_WORKERS"] = "0"
|
|
|
|
build_app
|
|
end
|
|
|
|
def teardown
|
|
ENV["PARALLEL_WORKERS"] = @old
|
|
|
|
teardown_app
|
|
end
|
|
|
|
test "simple successful test" do
|
|
app_file "test/unit/foo_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class FooTest < ActiveSupport::TestCase
|
|
def test_truth
|
|
assert true
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
assert_successful_test_run "unit/foo_test.rb"
|
|
end
|
|
|
|
test "after_run" do
|
|
app_file "test/unit/foo_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
Minitest.after_run { puts "WORLD" }
|
|
Minitest.after_run { puts "HELLO" }
|
|
|
|
class FooTest < ActiveSupport::TestCase
|
|
def test_truth
|
|
assert true
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
result = assert_successful_test_run "unit/foo_test.rb"
|
|
assert_equal ["HELLO", "WORLD"], result.scan(/HELLO|WORLD/) # only once and in correct order
|
|
end
|
|
|
|
test "simple failed test" do
|
|
app_file "test/unit/foo_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class FooTest < ActiveSupport::TestCase
|
|
def test_truth
|
|
assert false
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
assert_unsuccessful_run "unit/foo_test.rb", "Failure:\nFooTest#test_truth"
|
|
end
|
|
|
|
test "integration test" do
|
|
controller "posts", <<-RUBY
|
|
class PostsController < ActionController::Base
|
|
end
|
|
RUBY
|
|
|
|
app_file "app/views/posts/index.html.erb", <<-HTML
|
|
Posts#index
|
|
HTML
|
|
|
|
app_file "test/integration/posts_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class PostsTest < ActionDispatch::IntegrationTest
|
|
def test_index
|
|
get '/posts'
|
|
assert_response :success
|
|
assert_includes @response.body, 'Posts#index'
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
assert_successful_test_run "integration/posts_test.rb"
|
|
end
|
|
|
|
test "enable full backtraces on test failures" do
|
|
app_file "test/unit/failing_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class FailingTest < ActiveSupport::TestCase
|
|
def test_failure
|
|
raise "fail"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
output = run_test_file("unit/failing_test.rb", env: { "BACKTRACE" => "1" })
|
|
assert_match %r{test/unit/failing_test\.rb}, output
|
|
assert_match %r{test/unit/failing_test\.rb:4}, output
|
|
end
|
|
|
|
test "ruby schema migrations" do
|
|
output = rails("generate", "model", "user", "name:string")
|
|
version = output.match(/(\d+)_create_users\.rb/)[1]
|
|
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
User.create! name: "Jon"
|
|
end
|
|
end
|
|
RUBY
|
|
app_file "db/schema.rb", ""
|
|
|
|
assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
|
|
|
|
app_file "db/schema.rb", <<-RUBY
|
|
ActiveRecord::Schema.define(version: #{version}) do
|
|
create_table :users do |t|
|
|
t.string :name
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
app_file "config/initializers/disable_maintain_test_schema.rb", <<-RUBY
|
|
Rails.application.config.active_record.maintain_test_schema = false
|
|
RUBY
|
|
|
|
assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
|
|
|
|
File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
|
|
|
|
result = assert_successful_test_run("models/user_test.rb")
|
|
assert_not_includes result, "create_table(:users)"
|
|
end
|
|
|
|
test "sql structure migrations" do
|
|
output = rails("generate", "model", "user", "name:string")
|
|
version = output.match(/(\d+)_create_users\.rb/)[1]
|
|
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
User.create! name: "Jon"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
app_file "db/structure.sql", ""
|
|
app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY
|
|
Rails.application.config.active_record.schema_format = :sql
|
|
RUBY
|
|
|
|
assert_unsuccessful_run "models/user_test.rb", "Migrations are pending"
|
|
|
|
app_file "db/structure.sql", <<-SQL
|
|
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
|
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
|
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
|
|
INSERT INTO schema_migrations (version) VALUES ('#{version}');
|
|
SQL
|
|
|
|
app_file "config/initializers/disable_maintain_test_schema.rb", <<-RUBY
|
|
Rails.application.config.active_record.maintain_test_schema = false
|
|
RUBY
|
|
|
|
assert_unsuccessful_run "models/user_test.rb", "Could not find table 'users'"
|
|
|
|
File.delete "#{app_path}/config/initializers/disable_maintain_test_schema.rb"
|
|
|
|
assert_successful_test_run("models/user_test.rb")
|
|
end
|
|
|
|
test "sql structure migrations when adding column to existing table" do
|
|
output_1 = rails("generate", "model", "user", "name:string")
|
|
version_1 = output_1.match(/(\d+)_create_users\.rb/)[1]
|
|
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
User.create! name: "Jon"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
app_file "config/initializers/enable_sql_schema_format.rb", <<-RUBY
|
|
Rails.application.config.active_record.schema_format = :sql
|
|
RUBY
|
|
|
|
app_file "db/structure.sql", <<-SQL
|
|
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
|
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
|
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255));
|
|
INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
|
|
SQL
|
|
|
|
assert_successful_test_run("models/user_test.rb")
|
|
|
|
output_2 = rails("generate", "migration", "add_email_to_users")
|
|
version_2 = output_2.match(/(\d+)_add_email_to_users\.rb/)[1]
|
|
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
User.create! name: "Jon", email: "jon@doe.com"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
app_file "db/structure.sql", <<-SQL
|
|
CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL);
|
|
CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version");
|
|
CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "email" varchar(255));
|
|
INSERT INTO schema_migrations (version) VALUES ('#{version_1}');
|
|
INSERT INTO schema_migrations (version) VALUES ('#{version_2}');
|
|
SQL
|
|
|
|
assert_successful_test_run("models/user_test.rb")
|
|
end
|
|
|
|
# TODO: would be nice if we could detect the schema change automatically.
|
|
# For now, the user has to synchronize the schema manually.
|
|
# This test-case serves as a reminder for this use-case.
|
|
test "manually synchronize test schema after rollback" do
|
|
output = rails("generate", "model", "user", "name:string")
|
|
version = output.match(/(\d+)_create_users\.rb/)[1]
|
|
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
assert_equal ["id", "name"], User.columns_hash.keys
|
|
end
|
|
end
|
|
RUBY
|
|
app_file "db/schema.rb", <<-RUBY
|
|
ActiveRecord::Schema.define(version: #{version}) do
|
|
create_table :users do |t|
|
|
t.string :name
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
assert_successful_test_run "models/user_test.rb"
|
|
|
|
# Simulate `db:rollback` + edit of the migration file + `db:migrate`
|
|
app_file "db/schema.rb", <<-RUBY
|
|
ActiveRecord::Schema.define(version: #{version}) do
|
|
create_table :users do |t|
|
|
t.string :name
|
|
t.integer :age
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
assert_successful_test_run "models/user_test.rb"
|
|
|
|
rails "db:test:prepare"
|
|
|
|
assert_unsuccessful_run "models/user_test.rb", <<-ASSERTION
|
|
Expected: ["id", "name"]
|
|
Actual: ["id", "name", "age"]
|
|
ASSERTION
|
|
end
|
|
|
|
test "hooks for plugins" do
|
|
output = rails("generate", "model", "user", "name:string")
|
|
version = output.match(/(\d+)_create_users\.rb/)[1]
|
|
|
|
app_file "lib/tasks/hooks.rake", <<-RUBY
|
|
task :before_hook do
|
|
has_user_table = ActiveRecord::Base.connection.table_exists?('users')
|
|
puts "before: " + has_user_table.to_s
|
|
end
|
|
|
|
task :after_hook do
|
|
has_user_table = ActiveRecord::Base.connection.table_exists?('users')
|
|
puts "after: " + has_user_table.to_s
|
|
end
|
|
|
|
Rake::Task["db:test:prepare"].enhance [:before_hook] do
|
|
Rake::Task[:after_hook].invoke
|
|
end
|
|
RUBY
|
|
app_file "test/models/user_test.rb", <<-RUBY
|
|
require 'test_helper'
|
|
class UserTest < ActiveSupport::TestCase
|
|
test "user" do
|
|
User.create! name: "Jon"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
# Simulate `db:migrate`
|
|
app_file "db/schema.rb", <<-RUBY
|
|
ActiveRecord::Schema.define(version: #{version}) do
|
|
create_table :users do |t|
|
|
t.string :name
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
output = assert_successful_test_run "models/user_test.rb"
|
|
assert_includes output, "before: false\nafter: true"
|
|
|
|
# running tests again won't trigger a schema update
|
|
output = assert_successful_test_run "models/user_test.rb"
|
|
assert_not_includes output, "before:"
|
|
assert_not_includes output, "after:"
|
|
end
|
|
|
|
private
|
|
def assert_unsuccessful_run(name, message)
|
|
result = run_test_file(name)
|
|
assert_not_equal 0, $?.to_i
|
|
assert_includes result, message
|
|
result
|
|
end
|
|
|
|
def assert_successful_test_run(name)
|
|
result = run_test_file(name)
|
|
assert_equal 0, $?.to_i, result
|
|
result
|
|
end
|
|
|
|
def run_test_file(name, options = {})
|
|
rails "test", "#{app_path}/test/#{name}", allow_failure: true
|
|
end
|
|
end
|
|
end
|