mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Automatically maintain test database schema
* Move check from generated helper to test_help.rb, so that all applications can benefit * Rather than just raising when the test schema has pending migrations, try to load in the schema and only raise if there are pending migrations afterwards * Opt out of the check by setting config.active_record.maintain_test_schema = false * Deprecate db:test:* tasks. The test helper is now fully responsible for maintaining the test schema, so we don't need rake tasks for this. This is also a speed improvement since we're no longer reloading the test database on every call to "rake test".
This commit is contained in:
parent
a1d0c0fa3d
commit
ff7ab3bc78
17 changed files with 173 additions and 58 deletions
|
@ -1,3 +1,12 @@
|
|||
* Since the `test_help.rb` in Railties now automatically maintains
|
||||
your test schema, the `rake db:test:*` tasks are deprecated. This
|
||||
doesn't stop you manually running other tasks on your test database
|
||||
if needed:
|
||||
|
||||
rake db:schema:load RAILS_ENV=test
|
||||
|
||||
*Jon Leighton*
|
||||
|
||||
* Fix presence validator for association when the associated record responds to `to_a`.
|
||||
|
||||
*gmarik*
|
||||
|
@ -54,6 +63,8 @@
|
|||
* Deprecated use of string argument as a configuration lookup in
|
||||
`ActiveRecord::Base.establish_connection`. Instead, a symbol must be given.
|
||||
|
||||
* Deprecated use of string argument as a configuration lookup in `ActiveRecord::Base.establish_connection`. Instead, a symbol must be given.
|
||||
|
||||
*José Valim*
|
||||
|
||||
* Fixed `update_column`, `update_columns`, and `update_all` to correctly serialize
|
||||
|
|
|
@ -69,6 +69,9 @@ module ActiveRecord
|
|||
mattr_accessor :timestamped_migrations, instance_writer: false
|
||||
self.timestamped_migrations = true
|
||||
|
||||
# :nodoc:
|
||||
mattr_accessor :maintain_test_schema, instance_accessor: false
|
||||
|
||||
def self.disable_implicit_join_references=(value)
|
||||
ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \
|
||||
"Make sure to remove this configuration because it does nothing.")
|
||||
|
|
|
@ -389,6 +389,19 @@ module ActiveRecord
|
|||
raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
|
||||
end
|
||||
|
||||
def load_schema_if_pending!
|
||||
if ActiveRecord::Migrator.needs_migration?
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema
|
||||
check_pending!
|
||||
end
|
||||
end
|
||||
|
||||
def maintain_test_schema! # :nodoc:
|
||||
if ActiveRecord::Base.maintain_test_schema
|
||||
suppress_messages { load_schema_if_pending! }
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(name, *args, &block) # :nodoc:
|
||||
(delegate || superclass.delegate).send(name, *args, &block)
|
||||
end
|
||||
|
|
|
@ -31,22 +31,15 @@ module ActiveRecord
|
|||
|
||||
|
||||
config.active_record.use_schema_cache_dump = true
|
||||
config.active_record.maintain_test_schema = true
|
||||
|
||||
config.eager_load_namespaces << ActiveRecord
|
||||
|
||||
rake_tasks do
|
||||
require "active_record/base"
|
||||
|
||||
ActiveRecord::Tasks::DatabaseTasks.seed_loader = Rails.application
|
||||
ActiveRecord::Tasks::DatabaseTasks.env = Rails.env
|
||||
|
||||
namespace :db do
|
||||
task :load_config do
|
||||
ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first
|
||||
ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a
|
||||
ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures'
|
||||
ActiveRecord::Tasks::DatabaseTasks.root = Rails.root
|
||||
|
||||
configuration = if ENV["DATABASE_URL"]
|
||||
{ Rails.env => ENV["DATABASE_URL"] }
|
||||
else
|
||||
|
|
|
@ -234,9 +234,7 @@ db_namespace = namespace :db do
|
|||
|
||||
desc 'Load a schema.rb file into the database'
|
||||
task :load => [:environment, :load_config] do
|
||||
file = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb')
|
||||
ActiveRecord::Tasks::DatabaseTasks.check_schema_file(file)
|
||||
load(file)
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema(:ruby, ENV['SCHEMA'])
|
||||
end
|
||||
|
||||
task :load_if_ruby => ['db:create', :environment] do
|
||||
|
@ -281,10 +279,7 @@ db_namespace = namespace :db do
|
|||
|
||||
# desc "Recreate the databases from the structure.sql file"
|
||||
task :load => [:environment, :load_config] do
|
||||
filename = ENV['DB_STRUCTURE'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql")
|
||||
ActiveRecord::Tasks::DatabaseTasks.check_schema_file(filename)
|
||||
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
|
||||
ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename)
|
||||
ActiveRecord::Tasks::DatabaseTasks.load_schema(:sql, ENV['DB_STRUCTURE'])
|
||||
end
|
||||
|
||||
task :load_if_sql => ['db:create', :environment] do
|
||||
|
@ -294,8 +289,15 @@ db_namespace = namespace :db do
|
|||
|
||||
namespace :test do
|
||||
|
||||
task :deprecated do
|
||||
Rake.application.top_level_tasks.grep(/^db:test:/).each do |task|
|
||||
$stderr.puts "WARNING: #{task} is deprecated. The Rails test helper now maintains " \
|
||||
"your test schema automatically, see the release notes for details."
|
||||
end
|
||||
end
|
||||
|
||||
# desc "Recreate the test database from the current schema"
|
||||
task :load => 'db:test:purge' do
|
||||
task :load => %w(db:test:deprecated db:test:purge) do
|
||||
case ActiveRecord::Base.schema_format
|
||||
when :ruby
|
||||
db_namespace["test:load_schema"].invoke
|
||||
|
@ -305,7 +307,7 @@ db_namespace = namespace :db do
|
|||
end
|
||||
|
||||
# desc "Recreate the test database from an existent schema.rb file"
|
||||
task :load_schema => 'db:test:purge' do
|
||||
task :load_schema => %w(db:test:deprecated db:test:purge) do
|
||||
begin
|
||||
should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
||||
|
@ -319,7 +321,7 @@ db_namespace = namespace :db do
|
|||
end
|
||||
|
||||
# desc "Recreate the test database from an existent structure.sql file"
|
||||
task :load_structure => 'db:test:purge' do
|
||||
task :load_structure => %w(db:test:deprecated db:test:purge) do
|
||||
begin
|
||||
ActiveRecord::Tasks::DatabaseTasks.current_config(:config => ActiveRecord::Base.configurations['test'])
|
||||
db_namespace["structure:load"].invoke
|
||||
|
@ -329,7 +331,7 @@ db_namespace = namespace :db do
|
|||
end
|
||||
|
||||
# desc "Recreate the test database from a fresh schema"
|
||||
task :clone => :environment do
|
||||
task :clone => %w(db:test:deprecated environment) do
|
||||
case ActiveRecord::Base.schema_format
|
||||
when :ruby
|
||||
db_namespace["test:clone_schema"].invoke
|
||||
|
@ -339,18 +341,18 @@ db_namespace = namespace :db do
|
|||
end
|
||||
|
||||
# desc "Recreate the test database from a fresh schema.rb file"
|
||||
task :clone_schema => ["db:schema:dump", "db:test:load_schema"]
|
||||
task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema)
|
||||
|
||||
# desc "Recreate the test database from a fresh structure.sql file"
|
||||
task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ]
|
||||
task :clone_structure => %w(db:structure:dump db:test:load_structure)
|
||||
|
||||
# desc "Empty the test database"
|
||||
task :purge => [:environment, :load_config] do
|
||||
task :purge => %w(db:test:deprecated environment load_config) do
|
||||
ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test']
|
||||
end
|
||||
|
||||
# desc 'Check for pending migrations and load the test schema'
|
||||
task :prepare => [:environment, :load_config] do
|
||||
task :prepare => %w(db:test:deprecated environment load_config) do
|
||||
unless ActiveRecord::Base.configurations.blank?
|
||||
db_namespace['test:load'].invoke
|
||||
end
|
||||
|
@ -385,5 +387,3 @@ namespace :railties do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations']
|
||||
|
|
|
@ -36,9 +36,8 @@ module ActiveRecord
|
|||
module DatabaseTasks
|
||||
extend self
|
||||
|
||||
attr_writer :current_config
|
||||
attr_accessor :database_configuration, :migrations_paths, :seed_loader, :db_dir,
|
||||
:fixtures_path, :env, :root
|
||||
attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
|
||||
attr_accessor :database_configuration
|
||||
|
||||
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
|
||||
|
||||
|
@ -51,6 +50,30 @@ module ActiveRecord
|
|||
register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
|
||||
register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
|
||||
|
||||
def db_dir
|
||||
@db_dir ||= Rails.application.config.paths["db"].first
|
||||
end
|
||||
|
||||
def migrations_paths
|
||||
@migrations_paths ||= Rails.application.paths['db/migrate'].to_a
|
||||
end
|
||||
|
||||
def fixtures_path
|
||||
@fixtures_path ||= File.join(root, 'test', 'fixtures')
|
||||
end
|
||||
|
||||
def root
|
||||
@root ||= Rails.root
|
||||
end
|
||||
|
||||
def env
|
||||
@env ||= Rails.env
|
||||
end
|
||||
|
||||
def seed_loader
|
||||
@seed_loader ||= Rails.application
|
||||
end
|
||||
|
||||
def current_config(options = {})
|
||||
options.reverse_merge! :env => env
|
||||
if options.has_key?(:config)
|
||||
|
@ -133,6 +156,21 @@ module ActiveRecord
|
|||
class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
|
||||
end
|
||||
|
||||
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
||||
case format
|
||||
when :ruby
|
||||
file ||= File.join(db_dir, "schema.rb")
|
||||
check_schema_file(file)
|
||||
load(file)
|
||||
when :sql
|
||||
file ||= File.join(db_dir, "structure.sql")
|
||||
check_schema_file(file)
|
||||
structure_load(current_config, file)
|
||||
else
|
||||
raise ArgumentError, "unknown format #{format.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
def check_schema_file(filename)
|
||||
unless File.exist?(filename)
|
||||
message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
|
||||
|
|
|
@ -281,6 +281,13 @@ for detailed changes.
|
|||
* Add `Application#message_verifier` method to return a message
|
||||
verifier. ([Pull Request](https://github.com/rails/rails/pull/12995))
|
||||
|
||||
* The `test_help.rb` file which is required by the default generated test
|
||||
helper will automatically keep your test database up-to-date with
|
||||
`db/schema.rb` (or `db/structure.sql`). It raises an error if
|
||||
reloading the schema does not resolve all pending migrations. Opt out
|
||||
with `config.active_record.maintain_test_schema = false`. ([Pull
|
||||
Request](https://github.com/rails/rails/pull/13528))
|
||||
|
||||
Action Pack
|
||||
-----------
|
||||
|
||||
|
@ -421,6 +428,10 @@ for detailed changes.
|
|||
* Deprecated `ConnectionAdapters::SchemaStatements#distinct`,
|
||||
as it is no longer used by internals. ([Pull Request](https://github.com/rails/rails/pull/10556))
|
||||
|
||||
* Deprecated `rake db:test:*` tasks as the test database is now
|
||||
automatically maintained. See railties release notes. ([Pull
|
||||
Request](https://github.com/rails/rails/pull/13528))
|
||||
|
||||
### Notable changes
|
||||
|
||||
* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from
|
||||
|
|
|
@ -290,6 +290,8 @@ All these configuration options are delegated to the `I18n` library.
|
|||
|
||||
* `config.active_record.attribute_types_cached_by_default` sets the attribute types that `ActiveRecord::AttributeMethods` will cache by default on reads. The default is `[:datetime, :timestamp, :time, :date]`.
|
||||
|
||||
* `config.active_record.maintain_test_schema` is a boolean value which controls whether Active Record should try to keep your test database schema up-to-date with `db/schema.rb` (or `db/structure.sql`) when you run your tests. The default is true.
|
||||
|
||||
The MySQL adapter adds one additional configuration option:
|
||||
|
||||
* `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default.
|
||||
|
|
|
@ -224,7 +224,6 @@ and migrating the database. First, run:
|
|||
```bash
|
||||
$ cd test/dummy
|
||||
$ rake db:migrate
|
||||
$ rake db:test:prepare
|
||||
```
|
||||
|
||||
While you are here, change the Hickwall and Wickwall models so that they know that they are supposed to act
|
||||
|
|
|
@ -211,31 +211,9 @@ This line of code is called an _assertion_. An assertion is a line of code that
|
|||
|
||||
Every test contains one or more assertions. Only when all the assertions are successful will the test pass.
|
||||
|
||||
### Preparing your Application for Testing
|
||||
### Maintaining the test database schema
|
||||
|
||||
Before you can run your tests, you need to ensure that the test database structure is current. For this you can use the following rake commands:
|
||||
|
||||
```bash
|
||||
$ rake db:migrate
|
||||
...
|
||||
$ rake db:test:load
|
||||
```
|
||||
|
||||
The `rake db:migrate` above runs any pending migrations on the _development_ environment and updates `db/schema.rb`. The `rake db:test:load` recreates the test database from the current `db/schema.rb`. On subsequent attempts, it is a good idea to first run `db:test:prepare`, as it first checks for pending migrations and warns you appropriately.
|
||||
|
||||
NOTE: `db:test:prepare` will fail with an error if `db/schema.rb` doesn't exist.
|
||||
|
||||
#### Rake Tasks for Preparing your Application for Testing
|
||||
|
||||
| Tasks | Description |
|
||||
| ------------------------------ | ------------------------------------------------------------------------- |
|
||||
| `rake db:test:clone` | Recreate the test database from the current environment's database schema |
|
||||
| `rake db:test:clone_structure` | Recreate the test database from the development structure |
|
||||
| `rake db:test:load` | Recreate the test database from the current `schema.rb` |
|
||||
| `rake db:test:prepare` | Check for pending migrations and load the test schema |
|
||||
| `rake db:test:purge` | Empty the test database. |
|
||||
|
||||
TIP: You can see all these rake tasks and their descriptions by running `rake --tasks --describe`
|
||||
In order to run your tests, your test database will need to have the current structure. The test helper checks whether your test database has any pending migrations. If so, it will try to load your `db/schema.rb` or `db/structure.sql` into the test database. If migrations are still pending, an error will be raised.
|
||||
|
||||
### Running Tests
|
||||
|
||||
|
|
|
@ -91,6 +91,13 @@ secrets, you need to:
|
|||
|
||||
5. Restart your server.
|
||||
|
||||
### Changes to test helper
|
||||
|
||||
If your test helper contains a call to
|
||||
`ActiveRecord::Migration.check_pending!` this can be removed. The check
|
||||
is now done automatically when you `require 'test_help'`, although
|
||||
leaving this line in your helper is not harmful in any way.
|
||||
|
||||
### Changes in JSON handling
|
||||
|
||||
There are a few major changes related to JSON handling in Rails 4.1.
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
* `test_help.rb` now automatically checks/maintains your test datbase
|
||||
schema. (Use `config.active_record.maintain_test_schema = false` to
|
||||
disable.)
|
||||
|
||||
*Jon Leighton*
|
||||
|
||||
* Configure `secrets.yml` and `database.yml` to read configuration
|
||||
from the system environment by default for production.
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@ require 'rails/test_help'
|
|||
|
||||
class ActiveSupport::TestCase
|
||||
<% unless options[:skip_active_record] -%>
|
||||
ActiveRecord::Migration.check_pending!
|
||||
|
||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||
#
|
||||
# Note: You'll currently still have to declare fixtures explicitly in integration tests
|
||||
|
|
|
@ -15,6 +15,8 @@ if ENV["BACKTRACE"].nil?
|
|||
end
|
||||
|
||||
if defined?(ActiveRecord::Base)
|
||||
ActiveRecord::Migration.maintain_test_schema!
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
include ActiveRecord::TestFixtures
|
||||
self.fixture_path = "#{Rails.root}/test/fixtures/"
|
||||
|
|
|
@ -174,6 +174,15 @@ module ApplicationTests
|
|||
require "#{app_path}/config/environment"
|
||||
db_test_load_structure
|
||||
end
|
||||
|
||||
test 'db:test deprecation' do
|
||||
require "#{app_path}/config/environment"
|
||||
Dir.chdir(app_path) do
|
||||
output = `bundle exec rake db:migrate db:test:prepare 2>&1`
|
||||
assert_equal "WARNING: db:test:prepare is deprecated. The Rails test helper now maintains " \
|
||||
"your test schema automatically, see the release notes for details.\n", output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -187,7 +187,7 @@ module ApplicationTests
|
|||
def test_scaffold_tests_pass_by_default
|
||||
output = Dir.chdir(app_path) do
|
||||
`rails generate scaffold user username:string password:string;
|
||||
bundle exec rake db:migrate db:test:clone test`
|
||||
bundle exec rake db:migrate test`
|
||||
end
|
||||
|
||||
assert_match(/7 runs, 13 assertions, 0 failures, 0 errors/, output)
|
||||
|
@ -197,7 +197,7 @@ module ApplicationTests
|
|||
def test_scaffold_with_references_columns_tests_pass_by_default
|
||||
output = Dir.chdir(app_path) do
|
||||
`rails generate scaffold LineItems product:references cart:belongs_to;
|
||||
bundle exec rake db:migrate db:test:clone test`
|
||||
bundle exec rake db:migrate test`
|
||||
end
|
||||
|
||||
assert_match(/7 runs, 13 assertions, 0 failures, 0 errors/, output)
|
||||
|
|
|
@ -67,10 +67,55 @@ module ApplicationTests
|
|||
assert_match %r{/app/test/unit/failing_test\.rb}, output
|
||||
end
|
||||
|
||||
test "migrations" do
|
||||
output = script('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 !result.include?("create_table(:users)")
|
||||
end
|
||||
|
||||
private
|
||||
def assert_unsuccessful_run(name, message)
|
||||
result = run_test_file(name)
|
||||
assert_not_equal 0, $?.to_i
|
||||
assert result.include?(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 = {})
|
||||
|
@ -83,7 +128,7 @@ module ApplicationTests
|
|||
env["RUBYLIB"] = $:.join(':')
|
||||
|
||||
Dir.chdir(app_path) do
|
||||
`#{env_string(env)} #{Gem.ruby} #{args.join(' ')}`
|
||||
`#{env_string(env)} #{Gem.ruby} #{args.join(' ')} 2>&1`
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue