Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'simple_instance_of'

  • Loading branch information...
commit ead85099ac322da54f57eda1a77f77401b03c482 2 parents 61de96b + d52b9a6
Brian Takita authored
View
12 README.rdoc
@@ -3,6 +3,18 @@
RR (Double Ruby) is a test double framework that features a rich
selection of double techniques and a terse syntax.
+== Notes by Developer
+
+There has been a major architectural issues and refactorings involved with introducing the all_instances_of feature.
+Basically, I need to bind RR's dispaching to the class instead of the Eigenclass of each instance, just like Mocha does.
+Unfortunately, it's not obviously feasible (without an ObjectSpace lookup) to support all of RR's methods (such as mocking).
+
+Of course ObjectSpace is not readily supported in jRuby, since it causes general slowness in the intreperter.
+I'm of the opinion that test speed is more important than having mocks on all instances of a class.
+
+Thanks for your patience,
+- Brian
+
== More Information
=== Mailing Lists
* double-ruby-users@rubyforge.org
View
1  doc/todo.txt
@@ -1 +0,0 @@
-Remove all strategy classes and define a module with mock/stub/etc methods.
View
9 lib/rr.rb
@@ -29,8 +29,12 @@
require "#{dir}/rr/double_definitions/strategies/implementation/proxy"
require "#{dir}/rr/double_definitions/strategies/double_injection/double_injection_strategy"
require "#{dir}/rr/double_definitions/strategies/double_injection/instance"
-require "#{dir}/rr/double_definitions/strategies/double_injection/instance_of_class"
-require "#{dir}/rr/double_definitions/strategies/double_injection/any_instance_of_class"
+require "#{dir}/rr/double_definitions/strategies/double_injection/any_instance_of"
+require "#{dir}/rr/double_definitions/strategies/double_injection/new_instance_of"
+require "#{dir}/rr/adapters/rr_methods"
+require "#{dir}/rr/double_definitions/double_injections/instance"
+require "#{dir}/rr/double_definitions/double_injections/any_instance_of"
+require "#{dir}/rr/double_definitions/double_injections/new_instance_of"
require "#{dir}/rr/double_definitions/double_definition"
require "#{dir}/rr/injections/injection"
@@ -48,7 +52,6 @@
require "#{dir}/rr/double_definitions/double_definition_create_blank_slate"
require "#{dir}/rr/double_definitions/double_definition_create"
require "#{dir}/rr/double_definitions/child_double_definition_create"
-require "#{dir}/rr/adapters/rr_methods"
require "#{dir}/rr/double"
require "#{dir}/rr/double_matches"
View
8 lib/rr/adapters/rr_methods.rb
@@ -130,6 +130,14 @@ def received(subject)
RR::SpyVerificationProxy.new(subject)
end
+ def new_instance_of(*args, &block)
+ RR::DoubleDefinitions::DoubleInjections::NewInstanceOf.call(*args, &block)
+ end
+
+ def any_instance_of(*args, &block)
+ RR::DoubleDefinitions::DoubleInjections::AnyInstanceOf.call(*args, &block)
+ end
+
instance_methods.each do |name|
alias_method "rr_#{name}", name
end
View
27 lib/rr/double_definitions/double_definition_create.rb
@@ -1,19 +1,36 @@
module RR
module DoubleDefinitions
class DoubleDefinitionCreate # :nodoc
+ extend(Module.new do
+ def default_double_injection_strategy
+ @default_double_injection_strategy ||= lambda do |double_injection_create|
+ Strategies::DoubleInjection::Instance.new(double_injection_create)
+ end
+ end
+
+ def set_default_double_injection_strategy(strategy_lambda)
+ original_strategy_lambda = default_double_injection_strategy
+ begin
+ @default_double_injection_strategy = strategy_lambda
+ yield
+ ensure
+ @default_double_injection_strategy = original_strategy_lambda
+ end
+ end
+ end)
+
attr_reader :subject, :verification_strategy, :implementation_strategy, :double_injection_strategy
NO_SUBJECT = Object.new
include Space::Reader
def initialize
- @verification_strategy = nil
+ @verification_strategy = Strategies::Verification::Stub.new(self)
@implementation_strategy = Strategies::Implementation::Reimplementation.new(self)
- @double_injection_strategy = Strategies::DoubleInjection::Instance.new(self)
+ @double_injection_strategy = self.class.default_double_injection_strategy.call(self)
end
def call(method_name, *args, &handler)
- raise DoubleDefinitionCreateError if no_subject?
definition = DoubleDefinition.new(self)
verification_strategy.call(definition, method_name, args, handler)
implementation_strategy.call(definition, method_name, args, handler)
@@ -113,11 +130,11 @@ def strong(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
# DoubleInjection Strategies
def any_instance_of(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
- self.add_double_injection_strategy(::RR::DoubleDefinitions::Strategies::DoubleInjection::AnyInstanceOfClass, subject, method_name, &definition_eval_block)
+ self.add_double_injection_strategy(::RR::DoubleDefinitions::Strategies::DoubleInjection::AnyInstanceOf, subject, method_name, &definition_eval_block)
end
def instance_of(subject=NO_SUBJECT, method_name=nil, &definition_eval_block)
- self.add_double_injection_strategy(::RR::DoubleDefinitions::Strategies::DoubleInjection::InstanceOfClass, subject, method_name, &definition_eval_block)
+ self.add_double_injection_strategy(::RR::DoubleDefinitions::Strategies::DoubleInjection::NewInstanceOf, subject, method_name, &definition_eval_block)
end
end
end
View
28 lib/rr/double_definitions/double_injections/any_instance_of.rb
@@ -0,0 +1,28 @@
+module RR
+ module DoubleDefinitions
+ module DoubleInjections
+ class AnyInstanceOf
+ extend(Module.new do
+ include RR::Adapters::RRMethods
+
+ def call(subject_class, stubbed_methods=nil, &block)
+ ::RR::DoubleDefinitions::DoubleDefinitionCreate.set_default_double_injection_strategy(lambda do |double_definition_create|
+ ::RR::DoubleDefinitions::Strategies::DoubleInjection::AnyInstanceOf.new(double_definition_create)
+ end) do
+ if stubbed_methods
+ subject_class.class_eval do
+ stubbed_methods.each do |name, value|
+ value_proc = value.is_a?(Proc) ? value : lambda {value}
+ RR.stub(subject_class, name).returns(&value_proc)
+ end
+ end
+ else
+ block.call(subject_class)
+ end
+ end
+ end
+ end)
+ end
+ end
+ end
+end
View
16 lib/rr/double_definitions/double_injections/instance.rb
@@ -0,0 +1,16 @@
+module RR
+ module DoubleDefinitions
+ module DoubleInjections
+ class Instance
+ extend(Module.new do
+ include ::RR::Adapters::RRMethods
+
+ def call(double_method_name, *args, &definition_eval_block)
+ double_definition_create = DoubleDefinitions::DoubleDefinitionCreate.new
+ double_definition_create.send(double_method_name, *args, &definition_eval_block)
+ end
+ end)
+ end
+ end
+ end
+end
View
50 lib/rr/double_definitions/double_injections/new_instance_of.rb
@@ -0,0 +1,50 @@
+module RR
+ module DoubleDefinitions
+ module DoubleInjections
+ class NewInstanceOf
+ extend(Module.new do
+ include RR::Adapters::RRMethods
+ def call(subject, stubbed_methods={})
+ double_definition_create = DoubleDefinitionCreate.new.stub
+ stub(subject).new do |*args| # TODO: Need to stub allocate instead of new
+ subject = subject.allocate
+ add_stubbed_methods(subject, stubbed_methods)
+ add_method_chain_definition(subject, double_definition_create)
+ yield(subject) if block_given?
+ initialize_subject_instance(subject, args)
+ end
+ DoubleDefinitionCreateBlankSlate.new(double_definition_create)
+ end
+
+ protected
+ def add_stubbed_methods(subject_instance, stubbed_methods)
+ stubbed_methods.each do |name, value|
+ value_proc = value.is_a?(Proc) ? value : lambda {value}
+ stub(subject_instance, name).returns(&value_proc)
+ end
+ end
+
+ def add_method_chain_definition(subject_instance, double_definition_create)
+ implementation_strategy = double_definition_create.implementation_strategy
+ if implementation_strategy.method_name
+ stub(subject_instance).method_missing(
+ implementation_strategy.method_name,
+ *implementation_strategy.args,
+ &implementation_strategy.handler
+ )
+ end
+ end
+
+ def initialize_subject_instance(subject_instance, args)
+ if args.last.is_a?(ProcFromBlock)
+ subject_instance.__send__(:initialize, *args[0..(args.length-2)], &args.last)
+ else
+ subject_instance.__send__(:initialize, *args)
+ end
+ subject_instance
+ end
+ end)
+ end
+ end
+ end
+end
View
31 lib/rr/double_definitions/strategies/double_injection/any_instance_of.rb
@@ -0,0 +1,31 @@
+module RR
+ module DoubleDefinitions
+ module Strategies
+ module DoubleInjection
+ # This class is Deprecated.
+ # Calling instance_of will cause all instances of the passed in Class
+ # to have the Double defined.
+ #
+ # The following example mocks all User's valid? method and return false.
+ # mock.instance_of(User).valid? {false}
+ #
+ # The following example mocks and proxies User#projects and returns the
+ # first 3 projects.
+ # mock.instance_of(User).projects do |projects|
+ # projects[0..2]
+ # end
+ class AnyInstanceOf < DoubleInjectionStrategy
+ protected
+ def do_call
+ if !double_definition_create.no_subject? && !double_definition_create.subject.is_a?(Class)
+ raise ArgumentError, "instance_of only accepts class objects"
+ end
+# subject, method_name
+ double_injection = Injections::DoubleInjection.find_or_create(subject, method_name)
+ Double.new(double_injection, definition)
+ end
+ end
+ end
+ end
+ end
+end
View
40 lib/rr/double_definitions/strategies/double_injection/any_instance_of_class.rb
@@ -1,40 +0,0 @@
-module RR
- module DoubleDefinitions
- module Strategies
- module DoubleInjection
- # Calling any_instance_of will cause all instances of the passed in Class
- # to have the Double defined.
- #
- # The following example mocks all User's valid? method and return false.
- # mock.any_instance_of(User).valid? {false}
- #
- # The following example mocks and proxies User#projects and returns the
- # first 3 projects.
- # mock.any_instance_of(User).projects do |projects|
- # projects[0..2]
- # end
- class AnyInstanceOfClass < InstanceOfClass
- protected
- def do_call
- ObjectSpace.each_object(subject) do |object|
- add_double_to_instance(object)
- end
- super
- end
-
- def add_double_to_instance(instance, *args)
- double_injection = Injections::DoubleInjection.find_or_create((class << instance; self; end), method_name)
- Double.new(double_injection, definition)
- #####
- if args.last.is_a?(ProcFromBlock)
- instance.__send__(:initialize, *args[0..(args.length-2)], &args.last)
- else
- instance.__send__(:initialize, *args)
- end
- instance
- end
- end
- end
- end
- end
-end
View
18 .../strategies/double_injection/instance_of_class.rb → ...ns/strategies/double_injection/new_instance_of.rb
@@ -2,6 +2,7 @@ module RR
module DoubleDefinitions
module Strategies
module DoubleInjection
+ # This class is Deprecated.
# Calling instance_of will cause all instances of the passed in Class
# to have the Double defined.
#
@@ -13,21 +14,14 @@ module DoubleInjection
# mock.instance_of(User).projects do |projects|
# projects[0..2]
# end
- class InstanceOfClass < DoubleInjectionStrategy
- def initialize(*args)
- super
-
+ class NewInstanceOf < DoubleInjectionStrategy
+ protected
+ def do_call
if !double_definition_create.no_subject? && !double_definition_create.subject.is_a?(Class)
raise ArgumentError, "instance_of only accepts class objects"
end
- end
-
- protected
- def do_call
- instance_of_subject_double_definition_create = DoubleDefinitionCreate.new
- instance_of_subject_double_definition_create.stub(subject)
- instance_of_subject_double_definition_create.call(:new) do |*args|
- add_double_to_instance(subject.allocate, *args)
+ DoubleDefinitions::DoubleInjections::NewInstanceOf.call(subject) do |subject|
+ add_double_to_instance(subject, *args)
end
end
View
3  lib/rr/injections/method_missing_injection.rb
@@ -51,9 +51,10 @@ def reset
protected
def bind_method
+ subject_class_object_id = subject_class.object_id
subject_class.class_eval((<<-METHOD), __FILE__, __LINE__ + 1)
def method_missing(method_name, *args, &block)
- MethodDispatches::MethodMissingDispatch.new(self, method_name, args, block).call
+ MethodDispatches::MethodMissingDispatch.new(self, ObjectSpace._id2ref(#{subject_class_object_id}), method_name, args, block).call
end
METHOD
end
View
15 lib/rr/method_dispatches/method_missing_dispatch.rb
@@ -7,15 +7,14 @@ def original_method_missing_alias_name
end
end)
- attr_reader :subject, :method_name
- def initialize(subject, method_name, args, block)
- @subject, @method_name, @args, @block = subject, method_name, args, block
+ attr_reader :subject, :subject_class, :method_name
+ def initialize(subject, subject_class, method_name, args, block)
+ @subject, @subject_class, @method_name, @args, @block = subject, subject_class, method_name, args, block
end
def call
- if Injections::DoubleInjection.exists_by_subject?(subject, method_name)
+ if Injections::DoubleInjection.exists?(subject_class, method_name)
@double = find_double_to_attempt
-
if double
call_yields
return_value = extract_subject_from_return_value(call_implementation)
@@ -33,7 +32,7 @@ def call
end
def call_original_method
- Injections::DoubleInjection.find_or_create_by_subject(subject, method_name).dispatch_method_delegates_to_dispatch_original_method do
+ Injections::DoubleInjection.find_or_create(subject_class, method_name).dispatch_method_delegates_to_dispatch_original_method do
call_original_method_missing
end
end
@@ -45,7 +44,7 @@ def call_implementation
double.method_call(args)
call_original_method
else
- if double_injection = Injections::DoubleInjection.find_by_subject(subject, method_name)
+ if double_injection = Injections::DoubleInjection.find(subject_class, method_name)
double_injection.bind_method
# The DoubleInjection takes care of calling double.method_call
subject.__send__(method_name, *args, &block)
@@ -56,7 +55,7 @@ def call_implementation
end
def double_injection
- Injections::DoubleInjection.find_or_create_by_subject(subject, method_name)
+ Injections::DoubleInjection.find_or_create(subject_class, method_name)
end
end
end
View
2  lib/rr/space.rb
@@ -104,7 +104,7 @@ def reset_singleton_method_added_injections
end
Injections::SingletonMethodAddedInjection.instances.clear
end
-
+
def reset_recorded_calls
@recorded_calls.clear
end
View
12 spec/api/any_instance_of/all_instances_of_spec.rb
@@ -2,11 +2,13 @@
describe "all_instances_of" do
it "applies to instances instantiated before the Double expection was created" do
- subject_class = Class.new
- subject = subject_class.new
- stub.all_instances_of(subject_class) do |o|
- o.to_s {"Subject is stubbed"}
+ pending("Completion of all_instances_of") do
+ subject_class = Class.new
+ subject = subject_class.new
+ all_instances_of(subject_class) do |o|
+ o.to_s {"Subject is stubbed"}
+ end
+ subject.to_s.should == "Subject is stubbed"
end
- subject.to_s.should == "Subject is stubbed"
end
end
View
47 spec/api/any_instance_of/any_instance_of_spec.rb
@@ -1,12 +1,47 @@
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
describe "any_instance_of" do
- it "applies to instances instantiated before the Double expection was created" do
- subject_class = Class.new
- subject = subject_class.new
- stub.any_instance_of(subject_class) do |o|
- o.to_s {"Subject is stubbed"}
+ context "when passed a block" do
+ it "applies to instances instantiated before the Double expection was created" do
+ subject_class = Class.new
+ subject = subject_class.new
+ class_called = false
+ any_instance_of(subject_class) do |o|
+ stub(o).to_s {"Subject is stubbed"}
+ stub.proxy(o).class {|klass| class_called = true; klass}
+ stub(o).foobar {:baz}
+ end
+
+ subject.to_s.should == "Subject is stubbed"
+ subject.class.should == subject_class
+ class_called.should == true
+ subject.foobar.should == :baz
+
+ RR.reset
+
+ subject.to_s.should_not == "Subject is stubbed"
+ class_called = false
+ subject.class.should == subject_class
+ class_called.should == false
+ subject.should_not respond_to(:baz)
+ end
+ end
+
+ context "when passed a Hash" do
+ it "stubs methods (key) with the value on instances instantiated before the Double expection was created" do
+ subject_class = Class.new
+ subject = subject_class.new
+ subject.should_not respond_to(:baz)
+
+ any_instance_of(subject_class, :to_s => "Subject is stubbed", :foobar => lambda {:baz})
+
+ subject.to_s.should == "Subject is stubbed"
+ subject.foobar.should == :baz
+
+ RR.reset
+
+ subject.to_s.should_not == "Subject is stubbed"
+ subject.should_not respond_to(:baz)
end
- subject.to_s.should == "Subject is stubbed"
end
end
View
51 spec/api/new_instance_of/new_instance_of_spec.rb
@@ -1,15 +1,50 @@
require File.expand_path("#{File.dirname(__FILE__)}/../../spec_helper")
describe "new_instance_of" do
- it "applies to instances instantiated before the Double expection was created" do
- subject_class = Class.new
- existing_subject = subject_class.new
- stub.new_instance_of(subject_class) do |o|
- o.to_s {"Subject is stubbed"}
+ context "when passed a method chain" do
+ it "stubs the called method name with the given value" do
+ subject_class = Class.new
+ existing_subject = subject_class.new
+ new_instance_of(subject_class).foobar {:baz}
+ new_subject = subject_class.new
+
+ existing_subject.should_not respond_to(:foobar)
+
+ new_subject.foobar.should == :baz
end
- new_subject = subject_class.new
+ end
- existing_subject.to_s.should_not == "Subject is stubbed"
- new_subject.to_s.should == "Subject is stubbed"
+ context "when passed a block" do
+ it "applies to instances instantiated before the Double expection was created" do
+ subject_class = Class.new
+ existing_subject = subject_class.new
+ class_called = false
+ new_instance_of(subject_class) do |o|
+ stub(o).to_s {"Subject is stubbed"}
+ stub.proxy(o).class {|klass| class_called = true; klass}
+ end
+ new_subject = subject_class.new
+
+ existing_subject.to_s.should_not == "Subject is stubbed"
+
+ new_subject.to_s.should == "Subject is stubbed"
+ new_subject.class.should == subject_class
+ class_called.should be_true
+ end
+ end
+
+ context "when passed a Hash" do
+ it "stubs methods (key) with the value on instances instantiated before the Double expection was created" do
+ subject_class = Class.new
+ existing_subject = subject_class.new
+ new_instance_of(subject_class, :to_s => "Subject is stubbed", :foobar => lambda {:baz})
+ new_subject = subject_class.new
+
+ existing_subject.to_s.should_not == "Subject is stubbed"
+ existing_subject.should_not respond_to(:foobar)
+
+ new_subject.to_s.should == "Subject is stubbed"
+ new_subject.foobar.should == :baz
+ end
end
end
View
2  spec/rr/double_definitions/double_definition_create_spec.rb
@@ -162,7 +162,7 @@ def foobar(*args)
context "when not passed a class" do
it "raises an ArgumentError" do
lambda do
- double_definition_create.instance_of(Object.new)
+ double_definition_create.instance_of(Object.new).foobar
end.should raise_error(ArgumentError, "instance_of only accepts class objects")
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.