On Sunday 19 April 2009, Michael Schuerig wrote:
> In a Rails controller I set the scope on a model class in an around
> filter. I have defined expectations on the model classes, and
> ideally, I would add a further expectation for the scope. Is this
> already possible in some way? How would I go about adding support a
> scope expectation?

I've found a way to duck punch my way into Spec::Mocks::Proxy and 
Spec::Mocks::MessageExpectation to check in #matches that a scope 
expectation is met. I'm stuck now, because I don't see how to 
appropriately report a violated expectation. So far, the invasive 
changes to both classes are completely general; the Rails-specifics are 
patched in from outside. That's how it should be, of course. Can anyone 
suggest how to achieve this for reporting?

I've attached the changes I've made so far.

Michael

-- 
Michael Schuerig
mailto:[email protected]
http://www.schuerig.de/michael/

diff --git a/lib/spec/rails/extensions.rb b/lib/spec/rails/extensions.rb
index 989dfd5..8f3d767 100644
--- a/lib/spec/rails/extensions.rb
+++ b/lib/spec/rails/extensions.rb
@@ -2,6 +2,7 @@ require 'spec'
 
 require 'spec/rails/extensions/spec/runner/configuration'
 require 'spec/rails/extensions/spec/matchers/have'
+require 'spec/rails/extensions/spec/mocks/scope_expectation'
 
 require 'spec/rails/extensions/active_support/test_case'
 require 'spec/rails/extensions/active_record/base'
diff --git a/lib/spec/rails/extensions/spec/mocks/error_generator.rb b/lib/spec/rails/extensions/spec/mocks/error_generator.rb
new file mode 100644
index 0000000..6451493
--- /dev/null
+++ b/lib/spec/rails/extensions/spec/mocks/error_generator.rb
@@ -0,0 +1,9 @@
+module Spec # :nodoc:
+  module Mocks # :nodoc:
+    class ErrorGenerator # :nodoc:
+      
+      def raise_unexpected_scope_error()
+      end
+    end
+  end
+end
diff --git a/lib/spec/rails/extensions/spec/mocks/scope_expectation.rb b/lib/spec/rails/extensions/spec/mocks/scope_expectation.rb
new file mode 100644
index 0000000..5069fe6
--- /dev/null
+++ b/lib/spec/rails/extensions/spec/mocks/scope_expectation.rb
@@ -0,0 +1,43 @@
+
+require 'spec/mocks/message_expectation'
+
+module Spec #:nodoc:
+  module Mocks #:nodoc:
+
+    class ScopeExpectation
+      def initialize(proxy, expected_scopes)
+        @proxy, @expected_scopes = proxy, expected_scopes
+      end
+      def scope_matches?
+        match = @expected_scopes.all? { |key, scope|
+          actual = @proxy.target.send(:scope, key)
+          ok = actual == scope
+#debugger ### REMOVE
+          $stderr.puts "*** Scope mismatch\nExpected: #{scope.inspect},\nActual: #{actual.inspect}" unless ok
+          ok
+        }
+        match
+      end
+    end
+
+
+    module MessageExpectationScopeExtension
+      def self.included(base)
+        base.send(:alias_method, :matches_without_scope, :matches)
+        base.send(:alias_method, :matches, :matches_with_scope)
+      end
+      def within_scope(expected_scopes)
+        @scope_expectation = ScopeExpectation.new(@proxy, expected_scopes)
+        self
+      end
+      def matches_with_scope(*args, &block)
+        matches_without_scope(*args, &block) &&
+          (!...@scope_expectation || @scope_expectation.scope_matches?)
+      end
+    end
+  end
+end
+
+Spec::Mocks::MessageExpectation.class_eval do
+  include Spec::Mocks::MessageExpectationScopeExtension
+end
diff --git a/spec/spec/rails/mocks/mock_model_spec.rb b/spec/spec/rails/mocks/mock_model_spec.rb
index 72abf4b..8283d89 100644
--- a/spec/spec/rails/mocks/mock_model_spec.rb
+++ b/spec/spec/rails/mocks/mock_model_spec.rb
@@ -101,6 +101,20 @@ describe "mock_model" do
       mock_model(MockableModel).as_new_record.to_param.should be(nil)
     end
   end
-end
 
+  describe "#within_scope" do
+    before do
+      @find_scope = { :find => { :include => :associated_model } }
+      MockableModel.should_receive(:all).within_scope(@find_scope).and_return([])
+    end
+    it "should fail with an error for the wrong scope" do
+      MockableModel.all
+    end
 
+    it "should let the correct scope pass" do
+      MockableModel.send(:with_scope, @find_scope) do
+        MockableModel.all
+      end
+    end
+  end
+end
diff --git a/lib/spec/mocks/message_expectation.rb b/lib/spec/mocks/message_expectation.rb
index 5a57a0a..caaa02e 100644
--- a/lib/spec/mocks/message_expectation.rb
+++ b/lib/spec/mocks/message_expectation.rb
@@ -8,10 +8,11 @@ module Spec
       attr_accessor :error_generator
       protected :error_generator, :error_generator=
       
-      def initialize(error_generator, expectation_ordering, expected_from, sym, method_block, expected_received_count=1, opts={}, &implementation)
+      def initialize(error_generator, expectation_ordering, expected_from, proxy, sym, method_block, expected_received_count=1, opts={}, &implementation)
         @error_generator = error_generator
         @error_generator.opts = opts
         @expected_from = expected_from
+        @proxy = proxy
         @sym = sym
         @method_block = method_block
         @return_block = nil
@@ -313,8 +314,8 @@ module Spec
     end
     
     class NegativeMessageExpectation < MessageExpectation
-      def initialize(message, expectation_ordering, expected_from, sym, method_block)
-        super(message, expectation_ordering, expected_from, sym, method_block, 0)
+      def initialize(message, expectation_ordering, expected_from, proxy, sym, method_block)
+        super(message, expectation_ordering, expected_from, proxy, sym, method_block, 0)
       end
       
       def negative_expectation_for?(sym)
diff --git a/lib/spec/mocks/proxy.rb b/lib/spec/mocks/proxy.rb
index c21ca67..11afc3d 100644
--- a/lib/spec/mocks/proxy.rb
+++ b/lib/spec/mocks/proxy.rb
@@ -15,6 +15,8 @@ module Spec
         $rspec_mocks.add(nil) unless $rspec_mocks.nil?
       end
 
+      attr_reader :target
+
       def initialize(target, name=nil, options={})
         @target = target
         @name = name
@@ -43,7 +45,7 @@ module Spec
         if existing_stub = @stubs.detect {|s| s.sym == sym }
           expectation = existing_stub.build_child(expected_from, block_given?? block : nil, 1, opts)
         else
-          expectation = MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
+          expectation = MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, self, sym, block_given? ? block : nil, 1, opts)
         end
         @expectations << expectation
         @expectations.last
@@ -52,13 +54,13 @@ module Spec
       def add_negative_message_expectation(expected_from, sym, &block)
         __add sym
         warn_if_nil_class sym
-        @expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
+        @expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, self, sym, block_given? ? block : nil)
         @expectations.last
       end
 
       def add_stub(expected_from, sym, opts={}, &implementation)
         __add sym
-        @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, nil, :any, opts, &implementation)
+        @stubs.unshift MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, self, sym, nil, :any, opts, &implementation)
         @stubs.first
       end
       
_______________________________________________
rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to