[rspec-devel] Rails IntegrationTest and REL_0_6_3

Jerry West jerry.west at ntlworld.com
Wed Oct 4 13:28:30 EDT 2006


This is how I integrated ActionController::IntegrationTest with the 
REL_0_6_3 rspec_on_rails plugin.  Caveat - it works for me but I may 
have broken something I don't use.  Is there a test suite for 
rspec_on_rails?

I'd appreciate people checking it out, my controller specs are rather basic.

Files also attached for convenience.

1.  Edit spec/spec_helper.rb to delete the require of controller_mixin 
and to have SpecTestCase inherit from ActionController::IntegrationTest 
(which is itself a subclass of Test::Unit::TestCase).

Note that I remove controller_mixin.rb altogether.  Most of the file is 
superceded by IntegrationTest functionality and what little I need I put 
into rspec_on_rails.rb.  I also found the extra level of indirection 
confusing!  This is probably a philosophical argument and if the plugin 
authors feel it's necessary, no doubt it can be restored.

2. I also removed large chunks of rspec_on_rails.rb.  Here's a (very 
briefly) annotated version of the attachment...

require 'application'
 
silence_warnings { RAILS_ENV = "test" }
 
require 'active_record/fixtures'
require 'action_controller/test_process'
require 'action_controller/integration'
require 'spec'
 
module Spec
  module Runner
    # this comes from controller_mixin (Spec::ControllerContext) and is 
the only thing that does
    module ContextEval
      module ModuleMethods
        def controller_name(name=nil)
          @controller_name = name if name
          @controller_name
        end

        # from the original rspec_on_rails.rb; not sure about these, 
have'nt used/tested them
        def helper(name, &block)
          self.class.helper(name, &block)
        end
 
        def self.helper(name, &block)
          Spec::Runner::ExecutionContext.send :define_method, 
name.to_sym, &block
        end
      end

      # from the original, but remove everything that's now done by 
IntegrationTest
      module InstanceMethods
        attr_reader :controller
 
        def setup_with_controller(controller_name=nil)
          return unless controller_name
 
          @controller_class = 
"#{controller_name}_controller".camelize.constantize
          raise "Can't determine controller class for #{self.class}" if 
@controller_class.nil?
          @controller = @controller_class.new
          @controller_class.send(:define_method, :rescue_action) { |e| 
raise e }
        end

        # necessary to avoid the 'cache controller' problems of my first 
rspec-user email
        # basically, the cache is cleared by IntegrationTest::process 
(called by get, post etc)
        # but my specs didn't do any 'get's before testing the 
controller, so cache was invalid
        def teardown_with_controller
          ActionController::Base.clear_last_instantiation! if
            ActionController::Base.respond_to? :clear_last_instantiation!
        end 
      end
      
    end

    # exactly as per the original...
    class Context
      module RailsPluginClassMethods
        def fixture_path
          @fixture_path ||= RAILS_ROOT + '/spec/fixtures'
        end
        attr_writer :fixture_path
      end
      extend RailsPluginClassMethods
 
      # entry point into rspec
      # Keep it sync'ed!
      super_run = instance_method(:run)
      define_method :run do |reporter, dry_run|
        controller_name = @context_eval_module.instance_eval 
{@controller_name}
 
        setup_method = proc do
          setup_with_controller(controller_name)
        end
        setup_parts.unshift setup_method
 
        teardown_method = proc do
          teardown_with_controller
        end
        teardown_parts.unshift teardown_method
        super_run.bind(self).call(reporter, dry_run)
      end
    end # Context
 
  end
end

# using assert_XX in specs causes an nil.add_assertion error which may 
be related to
# the comment in IntegrationTest about Test::Unit::TestCase requiring at 
least one
# test case(???hmmm). Anyway we 'fix' this by overriding the function 
that caused
# the error (can you say 'hack'?) with no apparent side-effects but it 
has NOT been
# extensively tested.  If I knew what the assertion was being added to, 
I might offer
# a better fix
module Test
  module Unit
    class TestCase
      def add_assertion   # overide Test::Unit::Case as we don't use 
that mechanism
      end
    end
  end
end

# IntegrationTest factored TestResponseBehaviour out from TestResponse, 
otherwise this
# is much as per the original
module ActionController
  # augment execution_context_class
  module TestResponseBehavior    
    def render?(expected=nil)
      expected = expected.to_s unless expected.nil?
      # rendered_file(with_controller=false) 
[actionpack/lib/test_process.rb]
      rendered = expected ? rendered_file(!expected.include?('/')) : 
rendered_file
      expected.should_equal rendered # returns nil on success!
      true  # error will have been raised otherwise
    end
  end
end

# and I re-wrote should_have_tag to make its use clearer, functionality 
is the same 
class String
  def should_have_tag(*opts)
    return false unless opts
    raise(ArgumentError, 'usage: should_have_tag(:tag => "tag", 
:contents => /Paul/)') unless opts.size <= 2
    
    # accept first argument as String/Symbol tag 
    if opts[0].is_a?(String) || opts[0].is_a?(Symbol)
      if opts[1] && opts[1].is_a?(Hash)
        # add it to subsequent hash
        opts = opts.last.merge({ :tag => opts.first.to_s }) 
      else
        # turn it into a hash
        opts = { :tag => opts[0] }
      end
    else 
      opts = opts.first    # assume Hash
    end
    
    begin
      HTML::Document.new(self).find(opts).should_not_be_nil
    rescue
      self.should_include opts.inspect
    end
  end
end
# I first tried to replace this with assert_tag to avoid duplicating 
effort, but that went down
# too many ratholes (including the nil.add_assertion call above).  I 
still think it's worthwhile
# having assertions available, but the response-based ones hook too deep 
for my liking (i.e. they
# operate behind the scenes without any visible context from the 
specification).
 
# this has gone in REL_0_6_4 but presumably has been replaced by something
NilClass.sugarize_for_rspec!



------

If/when I have time, I'll try to make it work for REL_0_6_4; I'd 
appreciate some pointers about how to sugarize Nil though (unless it's 
now core - I haven't looked at the code yet).

Best wishes,
  Jerry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: spec_helper.rb
Type: application/x-ruby
Size: 654 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/rspec-devel/attachments/20061004/1d22a6cd/attachment.bin 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: rspec_on_rails.rb
Type: application/x-ruby
Size: 3622 bytes
Desc: not available
Url : http://rubyforge.org/pipermail/rspec-devel/attachments/20061004/1d22a6cd/attachment-0001.bin 


More information about the rspec-devel mailing list