rails 单元测试

rails 如何保证每次单元测试都清理上一个测试的数据。

我也想弄明白这个问题,google任何rails测试机制都找不到有营养的解释
查书后,看到一些有价值的东西
是讲ruby的测试怎么跑的
[code="ruby"]
module Unit
# If set to false Test::Unit will not automatically run at exit.
def self.run=(flag)
@run = flag
end

# Automatically run tests at exit?
def self.run?
  @run ||= false
end

end
end

at_exit do
unless $! || Test::Unit.run?
exit Test::Unit::AutoRunner.run
end
end
[/code]
当然,rails一定调用ruby的这个流程。
简单讲,注册at_exit这个例程,这个kernel方法,在执行结束,执行Test::Unit::AutoRunner.run
再相应执行:
[code="ruby"]
RUNNERS = {
:console => proc do |r|
require 'test/unit/ui/console/testrunner'
Test::Unit::UI::Console::TestRunner
end,
:gtk => proc do |r|
require 'test/unit/ui/gtk/testrunner'
Test::Unit::UI::GTK::TestRunner
end,
:gtk2 => proc do |r|
require 'test/unit/ui/gtk2/testrunner'
Test::Unit::UI::GTK2::TestRunner
end,
:fox => proc do |r|
require 'test/unit/ui/fox/testrunner'
Test::Unit::UI::Fox::TestRunner
end,
:tk => proc do |r|
require 'test/unit/ui/tk/testrunner'
Test::Unit::UI::Tk::TestRunner
end,
}
[/code]

看了这个源代码,就对ruby处理单元测试,大体了解。那么想知道Rails怎么做的只要,相应的看源代码就最好。
于是,找到rubyonrails的api
Class
ActiveSupport::TestCase

In: vendor/rails/railties/lib/test_help.rb vendor/rails/activesupport/lib/active_support/test_case.rb
下面有
* ActiveRecord::TestFixtures
* Rails::BacktraceFilterForTestUnit
* ActiveSupport::Testing::Default
* ActiveSupport::Testing::SetupAndTeardown
* ActiveSupport::Testing::Assertions
* ActiveSupport::Testing::Deprecation

而ActiveRecord::TestFixtures是我们要找的,需要加载数据的那个
继续读有如下方法:
[code="ruby"]
Methods

* included
* run_in_transaction?
* setup_fixtures
* teardown_fixtures

[/code]

继续读,就清楚多了,一个加载环境,一个回滚。所以,理论上讲如果,你不想加载数据,也可以从这里修改。
[code="ruby"]
def self.included(base)
base.class_eval do
setup :setup_fixtures
teardown :teardown_fixtures

     superclass_delegating_accessor :fixture_path
     superclass_delegating_accessor :fixture_table_names
     superclass_delegating_accessor :fixture_class_names
     superclass_delegating_accessor :use_transactional_fixtures
     superclass_delegating_accessor :use_instantiated_fixtures   # true, false, or :no_instances
     superclass_delegating_accessor :pre_loaded_fixtures

     self.fixture_table_names = []
     self.use_transactional_fixtures = false
     self.use_instantiated_fixtures = true
     self.pre_loaded_fixtures = false

     self.fixture_class_names = {}
   end

   base.extend ClassMethods
 end

[/code]

[code="ruby"]
def setup_fixtures
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?

   if pre_loaded_fixtures && !use_transactional_fixtures
     raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
   end

  @fixture_cache = {}
   @@already_loaded_fixtures ||= {}

   # Load fixtures once and begin transaction.
   if run_in_transaction?
     if @@already_loaded_fixtures[self.class]
       @loaded_fixtures = @@already_loaded_fixtures[self.class]
    else
      load_fixtures
       @@already_loaded_fixtures[self.class] = @loaded_fixtures
    end
     ActiveRecord::Base.connection.increment_open_transactions
     ActiveRecord::Base.connection.transaction_joinable = false
    ActiveRecord::Base.connection.begin_db_transaction
   # Load fixtures for every test.
   else
     Fixtures.reset_cache
     @@already_loaded_fixtures[self.class] = nil
     load_fixtures
   end

  # Instantiate fixtures for every test if requested.
   instantiate_fixtures if use_instantiated_fixtures
 end

[/code]

[code="ruby"]
def teardown_fixtures
return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
unless run_in_transaction?
Fixtures.reset_cache
end

   # Rollback changes if a transaction is active.
   if run_in_transaction? && ActiveRecord::Base.connection.open_transactions != 0
     ActiveRecord::Base.connection.rollback_db_transaction
     ActiveRecord::Base.connection.decrement_open_transactions
  end
   ActiveRecord::Base.clear_active_connections!
 end

[/code]

rails源代码里的几个注释,也很好的回答我想要问题。感慨一下,想要拿高程老大的5分,可真不容易啊 :P