Rails 3 + DataMapper-база данных не создана / уничтожена между тестами


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

Я вполне уверен, что это не то, что мы должны делать вручную, но, возможно, я ошибаюсь. Я удалил ActiveRecord из своего проекта и начал создавать модели в DataMapper. Все это работает, но я хочу написать модульные тесты для моих моделей (и функциональные для моих контроллеров). Однако моя тестовая база данных не очищается между тестовыми запусками (это легко проверить с помощью теста). АР заботится об этом для вас, но, похоже, ребята из DM не учли это в своем проекте dm-rails. В отчаянной попытке стереть все с чистого листа, я сбросил все таблицы в моей тестовой базе данных. Теперь вместо того, чтобы мои модульные тесты терпели неудачу из-за загрязнения среды, они терпят неудачу из-за отсутствия схемы. Глядя на доступные мне задачи rake, я не могу восстановить свою тестовую базу данных, не очистив также свою базу данных разработки. Я начинаю сходить с ума и надеюсь, что парень DM + Пользователь Rails 3 может подтолкнуть меня в нужном направлении.

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

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

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

1 2

1 ответ:

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

Создайте a .файл rake в разделе lib / tasks, называется что-то вроде test_db_setup.грабли:

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.РБ:

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 в настоящее время не поддерживает вложенные транзакции, так что ваша логика приложения, скорее всего, сломает разрыв. Усечение по-прежнему быстро и гораздо безопаснее.