2011-05-10 37 views
2

我會在這裏嘗試,因爲DM的郵件列表似乎沒有太多來自其他用戶的輸入。Rails 3 + DataMapper - 數據庫沒有在測試之間創建/銷燬

我相當肯定這不是我們必須手動完成的,但也許我錯了。我已經從我的項目中刪除了ActiveRecord,並開始在DataMapper中創建模型。這一切都工作,但我想爲我的模型編寫單元測試(併爲我的控制器功能)。但是,我的測試數據庫在測試運行之間沒有清理(通過測試很容易證明)。 AR爲你照顧這件事,但似乎DM傢伙在他們的dm-rails項目中沒有考慮到這一點。

在拼命擦拭板岩乾淨的嘗試中,我把所有的表都放在我的測試數據庫中。現在,而不是我的單元測試失敗,因爲環境骯髒,他們失敗,因爲架構不存在。查看可用的rake任務,如果不刪除我的開發數據庫,​​則無法恢復測試數據庫。我開始瘋了,希望一個DM + Rails 3用戶可以讓我朝正確的方向發展。

具體來說,當我運行我的單元測試時,應該在測試方法之間刪除所有測試數據。另外,如果我對模式進行了更改,我應該能夠運行我的測試,並且它們應該可以工作。我試着把DataMapper.auto_migrate!放在setup的回調函數test_helper.rb中,但是這似乎沒有創建模式(由於表格不存在,所以當他們嘗試插入/選擇記錄時,測試仍然失敗)。

我見過https://github.com/bmabey/database_cleaner,但是我們是否真的必須將外部庫引入Rails才能完成DM可能已經擁有(看似無證)支持的內容?這也不能解決重新創建模式的問題。

回答

1

答案回來的郵件列表上,這基本上是一個做自己動手的情況,所以給別人,如果他們最終不得不這樣做太節省了麻煩:

創建lib下一個.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:prepare鉤Rails提供,運行測試套件開始之前。它將架構從開發數據庫複製到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表類型支持嵌套事務,所以你的應用程序邏輯可能會破壞拆卸。截斷仍然很快,並且更安全。