2

残念ながら、DM のメーリング リストには他のユーザーからの情報があまりないように思われるので、ここで試してみます。

これは手動で行う必要はないと確信していますが、間違っている可能性があります。プロジェクトから ActiveRecord を削除し、DataMapper でモデルの作成を開始しました。すべてが機能していますが、モデルの単体テスト (およびコントローラーの機能) を作成したいと考えています。ただし、私のテスト データベースは、テストの実行間でクリーンアップされません (テストで簡単に証明されます)。AR がこれを処理しますが、DM 担当者は dm-rails プロジェクトでこれを考慮していないようです。

スレートを一掃する必死の試みで、テスト データベースのすべてのテーブルを削除しました。環境が汚れているために単体テストが失敗するのではなく、スキーマが存在しないために単体テストが失敗するようになりました。利用可能な rake タスクを見ると、開発データベースを消去しないとテスト DB を復元できません。私は正気を失い始めており、仲間の DM + Rails 3 ユーザーが私を正しい方向に向かわせてくれることを願っています。

具体的には、単体テストを実行するときに、テスト メソッド間ですべてのテスト データを削除する必要があります。また、スキーマに変更を加えた場合、テストを実行でき、動作するはずです。

test_helper.rb にコールバックを入れてみDataMapper.auto_migrate!ましたsetupが、スキーマが作成されていないようです (レコードを挿入/選択しようとしたときにテーブルが存在しないため、テストは失敗します)。

https://github.com/bmabey/database_cleanerを見たことがありますが、DMがおそらくすでに(文書化されていないように見える)サポートを持っていることを行うためだけに、Railsに外部ライブラリを持ち込む必要がありますか? これは、スキーマの再作成の問題にも対処しません。

4

1 に答える 1

1

答えはメーリングリストに戻ってきました。これは基本的に自分でやる状況なので、他の人もこれをしなければならない場合に面倒を省くためです:

lib/tasks の下に、test_db_setup.rake のような名前の .rake ファイルを作成します。

require File.dirname(__FILE__) + '/../../test/database_dumper'

# Custom logic that runs before the test suite begins
# This just clones the development database schema to the test database
# Note that each test does a lightweight teardown of just truncating all tables
namespace :db do

    namespace :test do
        desc "Reset the test database to match the development schema"
        task :prepare do
            Rake::Task['db:schema:clone'].invoke
        end
    end

    namespace :schema do
        desc "Literally dump the database schema into db/schema/**/*.sql"
        task :dump => :environment do
            DatabaseDumper.dump_schema(:directory => "#{Rails.root}/db/schema", :env => Rails.env)
        end

        desc "Clones the development schema into the test database"
        task :clone => [:dump, :environment] do
            DatabaseDumper.import_schema(:directory => "#{Rails.root}/db/schema", :env => "test")
        end
    end

end

task 'test:prepare' => 'db:test:prepare'

これは、:test:prepareRails が提供するフックを使用します。これは、テスト スイートが開始される直前に実行されます。スキーマを開発データベースから db/schema/ の下の .sql ファイル (テーブル/ビューごとに 1 つ) にコピーし、それらの .sql ファイルをテスト データベースにインポートします。

これを機能させるには、私が作成したユーティリティ クラスが必要です (現在、MySQL >= 5.0.1 用に作成されています。別のデータベースが必要な場合は、ロジックを調整する必要があります。

# Utility class for dumping and importing the database schema
class DatabaseDumper
    def self.dump_schema(options = {})
        options[:directory] ||= "#{Rails.root}/db/schema"
        options[:env]       ||= Rails.env

        schema_dir = options[:directory]

        clean_sql_directory(schema_dir)

        Rails::DataMapper.configuration.repositories[options[:env]].each do |repository, config|
            repository_dir = "#{schema_dir}/#{repository}"
            adapter        = DataMapper.setup(repository, config)

            perform_schema_dump(adapter, repository_dir)
        end
    end

    def self.import_schema(options = {})
        options[:directory] ||= "#{Rails.root}/db/schema"
        options[:env]       ||= "test"

        schema_dir = options[:directory]

        Rails::DataMapper.configuration.repositories[options[:env]].each do |repository, config|
            repository_dir = "#{schema_dir}/#{repository}"
            adapter        = DataMapper.setup(repository, config)

            perform_schema_import(adapter, repository_dir)
        end
    end

    private

        def self.clean_sql_directory(path)
            Dir.mkdir(path) unless Dir.exists?(path)
            Dir.glob("#{path}/**/*.sql").each do |file|
                File.delete(file)
            end
        end

        def self.perform_schema_dump(adapter, path)
            Dir.mkdir(path) unless Dir.exists?(path)

            adapter.select("SHOW FULL TABLES").each do |row|
                name    = row.values.first
                type    = row.values.last
                sql_dir = "#{path}/#{directory_name_for_table_type(type)}"

                Dir.mkdir(sql_dir) unless Dir.exists?(sql_dir)

                schema_info = adapter.select("SHOW CREATE TABLE #{name}").first

                sql = schema_info.values.last

                f = File.open("#{sql_dir}/#{name}.sql", "w+")
                f << sql << "\n"
                f.close
            end
        end

        def self.directory_name_for_table_type(type)
            case type
                when "VIEW"
                    "views"
                when "BASE TABLE"
                    "tables"
                else
                    raise "Unknown table type #{type}"
            end
        end

        def self.perform_schema_import(adapter, path)
            tables_dir     = "#{path}/tables"
            views_dir      = "#{path}/views"

            { "TABLE" => tables_dir, "VIEW" => views_dir }.each do |type, sql_dir|
                Dir.glob("#{sql_dir}/*.sql").each do |file|
                    name       = File.basename(file, ".sql")
                    drop_sql   = "DROP #{type} IF EXISTS `#{name}`"
                    create_sql = File.open(file, "r").read

                    adapter.execute(drop_sql)
                    adapter.execute(create_sql)
                end
            end
        end
end

これにより、.sql ファイルもスキーマ ディレクトリに残るため、参照が必要な場合は参照できます。

これで、テスト スイートの起動時に (新しいスキーマをインストールすることによって) データベースが消去されるだけです。テストメソッド間でテストを消去しません。そのためには、 DatabaseCleanerを使用する必要があります。それを test_helper.rb に入れます:

require 'database_cleaner'

DatabaseCleaner.strategy = :truncation, {:except => %w(auctionindexview helpindexview)}

class ActiveSupport::TestCase
    setup    :setup_database
    teardown :clean_database

    private

        def setup_database
            DatabaseCleaner.start
        end

        def clean_database
            DatabaseCleaner.clean
        end
end

これで準備完了です。テストの実行を開始すると、スキーマは最新の状態になり、SQL のコピーが db/schema ディレクトリに作成され、データはテスト メソッド間で消去されます。DatabaseCleaner のトランザクション戦略に惹かれている場合は、注意してください...これは MySQL で使用するのに安全な戦略となることはめったにありません。現在、MySQL テーブル タイプのいずれもネストされたトランザクションをサポートしていないため、アプリケーション ロジックがティアダウンを中断する可能性があります。 . Truncate は依然として高速であり、はるかに安全です。

于 2011-05-11T12:47:29.780 に答える