Rails 3 + DataMapper - база данных не создана / уничтожена между тестами - PullRequest
2 голосов
/ 10 мая 2011

Я попробую здесь, поскольку, к сожалению, список рассылки для DM не имеет большого вклада от других пользователей.

Я вполне уверен, что это не то, что мы должны делать вручную, но, возможно, я ошибаюсь. Я удалил ActiveRecord из своего проекта и начал создавать модели в DataMapper. Это все работает, но я хочу написать модульные тесты для моих моделей (и функционал для моих контроллеров). Однако моя тестовая база данных не очищается между тестовыми прогонами (это легко проверить с помощью теста). AR позаботится об этом за вас, но кажется, что парни из DM не учли это в своем проекте dm-rails.

В отчаянной попытке очистить планшет, я отбросил все таблицы в моей тестовой базе данных. Теперь вместо того, чтобы мои модульные тесты перестали работать, потому что среда грязная, они терпят неудачу, потому что схема не существует. Глядя на доступные мне грабли, я не могу восстановить свою тестовую базу данных, не стирая при этом свою базу данных разработки. Я начинаю сходить с ума и надеюсь, что другой пользователь DM + Rails 3 сможет подтолкнуть меня в правильном направлении.

В частности, когда я запускаю свои модульные тесты, все тестовые данные должны быть удалены между методами тестирования. Кроме того, если я внесу изменения в схему, я смогу выполнить свои тесты, и они должны работать.

Я пытался поместить DataMapper.auto_migrate! в обратный вызов setup в моем test_helper.rb, но, похоже, это не создает схему (тесты все еще не выполняются из-за отсутствия таблиц, когда они пытаются вставить / выбрать записи ).

Я видел https://github.com/bmabey/database_cleaner, но нужно ли нам действительно вводить внешнюю библиотеку в Rails только для того, чтобы сделать что-то, что DM, вероятно, уже имеет (казалось бы, недокументированную) поддержку? Это также не решает проблему воссоздания схемы.

1 Ответ

1 голос
/ 11 мая 2011

Ответ вернулся в список рассылки о том, что это в основном ситуация «сделай сам», поэтому, чтобы избавить других от хлопот, если им в конечном итоге придется сделать это тоже:

Создайте файл .rake в lib / tasks, который называется что-то вроде test_db_setup.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:prepare, предоставляемый Rails, который запускается непосредственно перед началом набора тестов. Он копирует схему из вашей базы данных разработки в файлы .sql в каталоге db / schema / (по одному на таблицу / представление), а затем импортирует эти файлы .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 в настоящее время не поддерживает вложенные транзакции, поэтому логика вашего приложения, скорее всего, сломает демонтаж , Усечение по-прежнему быстрое и намного безопаснее.

...