2

我会在这里尝试,因为不幸的是,DM 的邮件列表似乎没有太多来自其他用户的输入。

我有理由确定这不是我们必须手动执行的操作,但也许我错了。我已经从我的项目中删除了 ActiveRecord,并开始在 DataMapper 中创建模型。一切正常,但我想为我的模型编写单元测试(并为我的控制器提供功能)。但是,我的测试数据库在测试运行之间没有被清理(很容易通过测试证明)。AR 会为您解决这个问题,但似乎 DM 人员在他们的 dm-rails 项目中没有考虑到这一点。

在绝望地试图擦干净石板时,我删除了我的测试数据库中的所有表。现在,我的单元测试不是因为环境脏而失败,而是因为模式不存在而失败。查看可供我使用的 rake 任务,如果不擦除我的开发数据库,​​我就无法恢复我的测试数据库。我开始发疯了,希望 DM + Rails 3 的同胞可以将我推向正确的方向。

具体来说,当我运行单元测试时,应该在测试方法之间删除所有测试数据。此外,如果我对架构进行更改,我应该能够运行我的测试并且它们应该可以工作。

我尝试在我的 test_helper.rb 中放置DataMapper.auto_migrate!一个setup回调,但这似乎并没有创建模式(由于在尝试插入/选择记录时表不存在,测试仍然失败)。

我已经看过https://github.com/bmabey/database_cleaner,但是我们真的必须将一个外部库引入 Rails 只是为了做一些 DM 可能已经(似乎没有记录)支持的事情吗?这也没有解决重新创建模式的问题。

4

1 回答 1

1

邮件列表上的答案基本上是自己动手的情况,所以如果他们最终也不得不这样做,可以为其他人省去麻烦:

在 lib/tasks 下创建一个 .rake 文件,名为 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:prepareRails 提供的钩子,它在测试套件开始之前运行。它将模式从您的开发数据库复制到 db/schema/ 下的 .sql 文件(每个表/视图一个),然后将这些 .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

现在你应该可以走了。当您开始运行测试时,您的模式将是新鲜的,您将在 db/schema 目录中拥有 SQL 的副本,并且您的数据将在测试方法之间擦除。如果您被 DatabaseCleaner 的事务策略所吸引,请注意……这在 MySQL 中使用很少是安全的策略,因为目前没有任何 MySQL 表类型支持嵌套事务,因此您的应用程序逻辑可能会破坏拆解. 截断仍然很快,而且更安全。

于 2011-05-11T12:47:29.780 回答