提交 d7404388 编写于 作者: J Jon Leighton

Merge pull request #13528 from jonleighton/maintain_test_schema

Automatically maintain test database schema
* 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 Core
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 @@ def check_pending!
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 @@ class Railtie < Rails::Railtie # :nodoc:
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 @@ class DatabaseNotSupported < StandardError; end # :nodoc:
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 @@ def register_task(pattern, task)
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 @@ def structure_load(*arguments)
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.
......
* `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 @@
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 @@
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 @@ def db_test_load_structure
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 @@ def test_loading_only_yml_fixtures
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 @@ def test_scaffold_tests_pass_by_default
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 @@ def test_failure
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 @@ def ruby(*args)
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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册