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

    Join Date
    Mar 2003
    Posts
    174
    Rep Power
    12

    Can't get this Rspec test to pass


    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
    12
    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?

IMN logo majestic logo threadwatch logo seochat tools logo