1
0
Fork 0
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:
Jon Leighton 2013-12-29 11:05:04 +00:00
parent a1d0c0fa3d
commit ff7ab3bc78
17 changed files with 173 additions and 58 deletions

View file

@ -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

View file

@ -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.")

View file

@ -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

View file

@ -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

View file

@ -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']

View file

@ -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.}

View file

@ -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

View file

@ -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.

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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/"

View file

@ -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

View file

@ -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)

View file

@ -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