#1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2003
    Posts
    174
    Rep Power
    0

    Can't get this Rspec test to pass


    Hello,
    I'm completely new to Rspec testing and I'm finding it very difficult to mock specific objects.

    In this test, I have a before block setup as such:

    Code:
      before do
        setup_controller_for_warden
        controller.session[:operation_id] = 1
        @operator = Factory :operator
        sign_in :operator, @operator
                
        @persist_herd = Herd.new
        @persist_herd.operation_id = 1;
        @persist_herd.herd_name = 'Persisted Herd (herd_rations_controller_spec)'
        @persist_herd.save!
      end
    
      describe "GET new" do
        it "assigns a new herd_ration as @herd_ration" do
          HerdRation.stub(:new){ mock_herd_ration }
          Herd.stub(:find).with(1) { @persistant_herd }
          
          get :new, :herd_id => 1
          assigns(:herd_ration).should be(mock_herd_ration)
          response.should be_success
        end
      end
    
    
    and here's my controller method:
    
      def new
        @herd_ration = HerdRation.new
        @herd = Herd.find(params[:herd_id])
        
        if @herd
          respond_with(@herd_rations, :layout=> !request.xhr?)
        else
          redirect_to(root_url, :notice => 'No herd selected for ration.')
        end
      end
    Here's the catch - we have this groovy little plugin that globally enforces a model scope via a session id, in this case: session[peration_id].

    Here's the code for the plugin:

    Code:
    module ApplicationScopeManager
    
    
       mattr_accessor :global_scope
       @@global_scope = []
    
       mattr_accessor :local_scope
       @@local_scope = {}
    
       def self.included(base)
          base.send :extend, ClassMethods
       end
    
       def self.setup
          yield self
       end
    
       module ClassMethods
    
    
          def method_missing(name, *args, &block)
    
             return acts_as_scope_manager($1, args) if name.to_s =~ /^acts_as_scope_manager_for_(.*)/
             
             return honors_scope_of($1, args) if name.to_s =~ /^honors_scope_of_(.*)/
             
             return current_scope_for($1) if name.to_s =~ /^current_scope_for_(.*)/
             
             super
          end
    
          def current_scope_for(scope)
             Thread.current[scope.to_sym]
          end
    
          def acts_as_scope_manager(scope, *args)
             options = args[0].extract_options! if args
             send :include, InstanceMethods
    
             set_global_scope(scope,options[:with_global_scope]) if options[:with_global_scope]
    
             send :before_filter, "set_scope_for_#{scope}".to_sym if self.ancestors.include? ActionController::Base
             send :default_scope, where(scope.to_sym => send("current_scope_for_#{scope}".to_sym)[:finder_scope]).create_with(scope.to_sym =>
             send("current_scope_for_#{scope}".to_sym)[:creator_scope]) if (self.ancestors.include? ActiveRecord::Base) && Thread.current[scope.to_sym]
          end
    
          alias honors_scope_of acts_as_scope_manager
    
          private
    
    
          def set_global_scope(scope, *args)
             Thread.current[scope.to_sym] = {:global_scope => args}
          end
    
       end
    
       module InstanceMethods
    
          def method_missing(name, *args, &block)
    
             return set_scope_for($1, args) if name.to_s =~ /^set_scope_for_(.*)/
    
             super
          end
    
          private
    
          def set_scope_for(scope, *args)
             if session[scope].class == Array
                application_scope = {:finder_scope => session[scope], :creator_scope => nil }
             else
                application_scope = {:finder_scope => [session[scope]], :creator_scope => session[scope] }
             end
    
             application_scope[:finder_scope] = application_scope[:finder_scope] + Thread.current[scope.to_sym][:global_scope] if Thread.current[scope.to_sym][:global_scope]
    
             Thread.current[scope.to_sym] = application_scope
    
          end
    
       end
    end
    This plugin enforces any model that calls the honors_scope_of_operation_id to include a lookup of the session[peration_id] in all database calls. For instance if I do a Model.find(params[:id]) it will automatically add a WHERE "field_name"."operation_id" = to the where clause. The problem I'm having is that I can't seem to mock the session[peration_id] in my Rspec tests. I thought setting it this way controller.session[peration_id] = 1 in the before block would have been sufficient.

    Any ideas?
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Mar 2003
    Posts
    174
    Rep Power
    0
    Hello, I found a solution that makes this test pass and it was simply to mock out the controller's find method as such:

    Herd.stub(:find).with(@persist_herd.id) { mock_herd }

    I am probably not fully understanding RSpec but this feels strange to me. In my understanding of writing tests, you want to first prepare the test environment by creating the proper database/objects relevant to the testing environment, pass the relevant parameters and test the results of method you are testing. Mocking out this controller code
    Code:
     @herd = Herd.find(params[:herd_id])
    with this
    Code:
    Herd.stub(:find).with(@persist_herd.id) { mock_herd }
    doesn't seem to let the controller test it's own code? Why can't the parameters I'm passing to the controller be enough to test this:
    Code:
     Herd.find(params[:herd_id])
    Please help me understand this?
    Last edited by CLEM_C_ROCK; July 14th, 2011 at 09:51 AM.

IMN logo majestic logo threadwatch logo seochat tools logo