`
firebody
  • 浏览: 40406 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

PATCH for restore fixtures data after every test running

阅读更多
rails的fixtures有一个令人讨厌的地方:

fixtures 的数据不会在测试结束后自动清除 ,这样就使得fixtures遗留的数据影响到后来的测试。

相关的争论也持续了很久 ,具体的连接请看  http://dev.rubyonrails.org/ticket/2404

里面的 Rick的patch我在rspec下用了,不见好用,我只能自己搞了一个patch,在rspec的 spec_helper.rb下引入
,解决了这个问题 。

patch主要的思路是: 每次测试setup运行前插入fixture的数据,保证这个插入 fixtures数据的事务和test运行时的事务是同一个 ,teardown结束前,清空一些类变量, 原有的teardown运行的时候会作事务回滚的动作,这样就可以保证每次测试都回滚圆来插入的 fixtures的数据。

相关的配置 :
  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures  = false



module Test #:nodoc:
  module Unit #:nodoc:
    class TestCase #:nodoc:
      alias_method : old_setup_with_fixtures, :setup_with_fixtures unless method_defined?(: old_setup_with_fixtures)
      alias_method : old_teardown_with_fixtures, :teardown_with_fixtures unless method_defined?(: old_teardown_with_fixtures)
      def setup_with_fixtures

        if use_transactional_fixtures?

          ActiveRecord::Base.send :increment_open_transactions
          ActiveRecord::Base.connection.begin_db_transaction
          close_original_activerecord_transaction_methods
        end
        old_setup_with_fixtures
        if use_transactional_fixtures?
          open_original_activerecord_transaction_methods
        end
        
      
      end
      
      def teardown_with_fixtures
        if use_transactional_fixtures?
          clear_fixtures_states_when_use_transactional_fixtures
        end
        old_teardown_with_fixtures       
      end      
      #prevent the next code:alias_method from trigger invoking the self.method_added introspected method
      class<<TestCase
        alias old_method_added method_added
        def method_added(m) 
          #do nothing
        end
      end

      alias_method :setup,:setup_with_fixtures
      alias_method :teardown,:teardown_with_fixtures
      #reopen the introspector class method:method_added
      class<<TestCase
        alias method_added old_method_added
      		
      end
      
      private
      def clear_fixtures_states_when_use_transactional_fixtures
        @@already_loaded_fixtures.clear if @@already_loaded_fixtures
        @loaded_fixtures.clear if  @loaded_fixtures

      end
      def close_original_activerecord_transaction_methods
        self.class.class_eval(%Q[
          	class<<ActiveRecord::Base
          		alias old_increment_open_transactions increment_open_transactions
          		def increment_open_transactions
          			#do nothing
      			end
      		end
          ])
        ActiveRecord::Base.connection.class.class_eval(%Q[
          	alias_method : old_begin_db_transaction,:begin_db_transaction
          	def begin_db_transaction
          		#do nothing
      		end
          ])
        
      end
      
      def open_original_activerecord_transaction_methods
        self.class.class_eval(%Q[
          	class<<ActiveRecord::Base
          		alias increment_open_transactions old_increment_open_transactions
          		
      		end
          ])
        ActiveRecord::Base.connection.class.class_eval(%Q[
          	alias_method :begin_db_transaction,: old_begin_db_transaction
          	
          ])
      end
    end
  end
end
分享到:
评论
3 楼 firebody 2007-12-13  
Readonly 写道
你这个方法好,在9月份的时候有过类似讨论:
http://www.iteye.com/topic/51922

你可以把这个方法提交到ror的issue tracker上了。
发上去了。两年多了,居然没有引起足够的注意。
看来rails 测试的观点和方式很不一样啊。
2 楼 Readonly 2007-12-11  
你这个方法好,在9月份的时候有过类似讨论:
http://www.iteye.com/topic/51922

你可以把这个方法提交到ror的issue tracker上了。
1 楼 firebody 2007-12-09  
更新了 rails 2.0.1后,跑 rspec 发现一大堆失败,看 activerecord的fixtures.rb代码,发现新版的 activerecord为了提升性能,在Fixtures里用一个类变量缓存了connection, 意味着所有的关于fixtures的插入都是使用同一个connection, 走我的代码的逻辑的话,会因为这个cached connection已经被rollback而导致后续的测试都失败。 还好 Fixtures类也相应增加了一个类方法 : reset_cache. 只要在teardown_with_fixtures调用这个reset_cache就行了。

修改后的代码 如下:
module Test #:nodoc:
  module Unit #:nodoc:
    class TestCase #:nodoc:
      alias_method : old_setup_with_fixtures, :setup_with_fixtures unless method_defined?(: old_setup_with_fixtures)
      alias_method : old_teardown_with_fixtures, :teardown_with_fixtures unless method_defined?(: old_teardown_with_fixtures)
      def setup_with_fixtures
        if use_transactional_fixtures?

          ActiveRecord::Base.send :increment_open_transactions
          ActiveRecord::Base.connection.begin_db_transaction
          close_original_activerecord_transaction_methods
        end
        old_setup_with_fixtures
        if use_transactional_fixtures?
          open_original_activerecord_transaction_methods
        end
        
      
      end
      
      def teardown_with_fixtures
        if use_transactional_fixtures?
        	Fixtures.send :reset_cache  if Fixtures.respond_to?(:reset_cache)
          clear_fixtures_states_when_use_transactional_fixtures
        end
        old_teardown_with_fixtures       
      end      
      #prevent the next code:alias_method from trigger invoking the self.method_added introspected method
      class<<TestCase
        alias old_method_added method_added
        def method_added(m) 
          #do nothing
        end
      end

      alias_method :setup,:setup_with_fixtures
      alias_method :teardown,:teardown_with_fixtures
      #reopen the introspector class method:method_added
      class<<TestCase
        alias method_added old_method_added
      		
      end
      
      private
      def clear_fixtures_states_when_use_transactional_fixtures
        @@already_loaded_fixtures.clear if @@already_loaded_fixtures
        @loaded_fixtures.clear if  @loaded_fixtures

      end
      def close_original_activerecord_transaction_methods
        self.class.class_eval(%Q[
          	class<<ActiveRecord::Base
          		alias old_increment_open_transactions increment_open_transactions
          		alias old_decrement_open_transactions decrement_open_transactions
          		def increment_open_transactions
          			#do nothing
      			end
      			def decrement_open_transactions
          			#do nothing
      			end
      		end
          ])
        ActiveRecord::Base.connection.class.class_eval(%Q[
          	alias_method : old_begin_db_transaction,:begin_db_transaction
          	alias_method : old_commit_db_transaction,:commit_db_transaction
          	def begin_db_transaction
          		#do nothing
      		end
      		def commit_db_transaction
          		#do nothing
      		end
          ])
        
      end
      
      def open_original_activerecord_transaction_methods
        self.class.class_eval(%Q[
          	class<<ActiveRecord::Base
          		alias increment_open_transactions old_increment_open_transactions
          		alias decrement_open_transactions old_decrement_open_transactions
      		end
          ])
        ActiveRecord::Base.connection.class.class_eval(%Q[
          	alias_method :begin_db_transaction,: old_begin_db_transaction
          	alias_method :commit_db_transaction,: old_commit_db_transaction
          	
          ])
      end
    end
  end
end

相关推荐

Global site tag (gtag.js) - Google Analytics