lib/caricature/isolation.rb in caricature-0.7.6 vs lib/caricature/isolation.rb in caricature-0.7.7

- old
+ new

@@ -1,144 +1,144 @@ -require File.dirname(__FILE__) + '/isolator' -require File.dirname(__FILE__) + '/method_call_recorder' -require File.dirname(__FILE__) + '/expectation' -require File.dirname(__FILE__) + '/verification' - -# Caricature - Bringing simple mocking to the DLR -# =============================================== -# -# This project aims to make interop between IronRuby objects and .NET objects easier. -# The idea is that it integrates nicely with bacon and later rspec and that it transparently lets you mock ironruby ojbects -# as well as CLR objects/interfaces. -# Caricature handles interfaces, interface inheritance, CLR objects, CLR object instances, Ruby classes and instances of Ruby classes. -# -# From the start I wanted to do away with names like mock, stub, record, replay, verify etc. -# Instead I took the advice from Roy Osherhove and went with a name of Isolation for creating a mock. -# -# An Isolation will create what in Rhino.Mocks would be called a DynamicMock (but can be a partial too) :) -# In Moq it would be the Loose mocking strategy. -# -# The naming of the methods for creating mocks is the one that JP Boodhoo proposed WhenToldTo and WasToldTo. -# To specify a stub/expectation on an isolation you have one and only one way of doing that with the method called when_receiving. -# Then only if you're interested in asserting if a method has been called you can use the did_receive? method for this. -# -# -# isolation = Isolation.for(Ninja) -# isolation.when_receiving(:attack) do |exp| -# exp.with(:shuriken) -# exp.return(3) -# end -# -# Battle.new(mock) -# battle.combat -# -# isolation.did_receive?(:attack).should.be.true? -# -# -# It may be very important to note that when you're going to be isolating CLR classes to be used in other CLR classes -# you still need to obide by the CLR rules. That means if you want to redefine a method you'll need to make sure it's -# marked as virtual. Static types are still governed by static type rules. I'm working on a solution around those -# problems but for the time being this is how it has to be. -module Caricature - - # The context for an isolator. This contains the +subject+, +recorder+, +expectations+ and +messenger+ - IsolatorContext = Struct.new(:subject, :recorder, :expectations, :messenger) - - # Instead of using confusing terms like Mocking and Stubbing which is basically the same. Caricature tries - # to unify those concepts by using a term of *Isolation*. - # When you're testing you typically want to be in control of what you're testing. To do that you isolate - # dependencies and possibly define return values for methods on them. - # At a later stage you might be interested in which method was called, maybe even with which parameters - # or you might be interested in the amount of times it has been called. - class Isolation - - # the real instance of the isolated subject - # used to forward calls in partial mocks - attr_accessor :instance - - # the method call recorder - attr_reader :recorder - - # the expecations set for this isolation - attr_reader :expectations - - # Initializes a new instance of this isolation. - def initialize(isolator, context) - @instance = isolator.subject - @messenger = context.messenger - @messenger.recorder = @recorder = context.recorder - @expectations = context.expectations - @proxy = isolator.isolation - isolator.isolation.class.instance_variable_set("@___context___", self) - end - - # record and send the message to the isolation. - # takes care of following expectations rules when sending messages. - def send_message(method_name, return_type, *args, &b) - @messenger.deliver(method_name, return_type, *args, &b) - end - - # record and send the message to the isolation. - # takes care of following expectations rules when sending messages. - def send_class_message(method_name, return_type, *args, &b) - @messenger.deliver_to_class(method_name, return_type, *args, &b) - end - - # builds up an expectation for an instance method, allows for overriding the result returned by the method - def create_override(method_name, &block) - internal_create_override method_name, :instance, &block - end - - # builds up an expectation for a class method, allows for overriding the result returned by the class method - def create_class_override(method_name, &block) - internal_create_override method_name, :class, &block - end - - # asserts whether the method has been called for the specified configuration - def verify(method_name, &block) - internal_verify method_name, :instance, &block - end - - # asserts whether the method has been called for the specified configuration - def class_verify(method_name, &block) - internal_verify method_name, :class, &block - end - - class << self - - # Creates an isolation object complete with proxy and method call recorder - # It works out which isolation it needs to create and provide and initializes the - # method call recorder - def for(subject, recorder = MethodCallRecorder.new, expectations = Expectations.new) - context = IsolatorContext.new subject, recorder, expectations - - isolator = RubyIsolator.for context - isolation = new(isolator, context) - isolator.isolation - end - - end - - protected - - def internal_create_override(method_name, mode=:instance, &block) - builder = ExpectationBuilder.new method_name - block.call builder unless block.nil? - exp = builder.build - expectations.add_expectation exp, mode - exp - end - - def internal_verify(method_name, mode=:instance, &block) - verification = Verification.new(method_name, recorder, mode) - block.call verification unless block.nil? - verification - end - end - - # +Mock+ is a synonym for +Isolation+ so you can still use it if you're that way inclined - Mock = Isolation unless defined? Mock - - # +Stub+ is a synonym for +Isolation+ so you can still use it if you're that way inclined - Stub = Isolation unless defined? Stub - +require File.dirname(__FILE__) + '/isolator' +require File.dirname(__FILE__) + '/method_call_recorder' +require File.dirname(__FILE__) + '/expectation' +require File.dirname(__FILE__) + '/verification' + +# Caricature - Bringing simple mocking to the DLR +# =============================================== +# +# This project aims to make interop between IronRuby objects and .NET objects easier. +# The idea is that it integrates nicely with bacon and later rspec and that it transparently lets you mock ironruby ojbects +# as well as CLR objects/interfaces. +# Caricature handles interfaces, interface inheritance, CLR objects, CLR object instances, Ruby classes and instances of Ruby classes. +# +# From the start I wanted to do away with names like mock, stub, record, replay, verify etc. +# Instead I took the advice from Roy Osherhove and went with a name of Isolation for creating a mock. +# +# An Isolation will create what in Rhino.Mocks would be called a DynamicMock (but can be a partial too) :) +# In Moq it would be the Loose mocking strategy. +# +# The naming of the methods for creating mocks is the one that JP Boodhoo proposed WhenToldTo and WasToldTo. +# To specify a stub/expectation on an isolation you have one and only one way of doing that with the method called when_receiving. +# Then only if you're interested in asserting if a method has been called you can use the did_receive? method for this. +# +# +# isolation = Isolation.for(Ninja) +# isolation.when_receiving(:attack) do |exp| +# exp.with(:shuriken) +# exp.return(3) +# end +# +# Battle.new(mock) +# battle.combat +# +# isolation.did_receive?(:attack).should.be.true? +# +# +# It may be very important to note that when you're going to be isolating CLR classes to be used in other CLR classes +# you still need to obide by the CLR rules. That means if you want to redefine a method you'll need to make sure it's +# marked as virtual. Static types are still governed by static type rules. I'm working on a solution around those +# problems but for the time being this is how it has to be. +module Caricature + + # The context for an isolator. This contains the +subject+, +recorder+, +expectations+ and +messenger+ + IsolatorContext = Struct.new(:subject, :recorder, :expectations, :messenger) + + # Instead of using confusing terms like Mocking and Stubbing which is basically the same. Caricature tries + # to unify those concepts by using a term of *Isolation*. + # When you're testing you typically want to be in control of what you're testing. To do that you isolate + # dependencies and possibly define return values for methods on them. + # At a later stage you might be interested in which method was called, maybe even with which parameters + # or you might be interested in the amount of times it has been called. + class Isolation + + # the real instance of the isolated subject + # used to forward calls in partial mocks + attr_accessor :instance + + # the method call recorder + attr_reader :recorder + + # the expecations set for this isolation + attr_reader :expectations + + # Initializes a new instance of this isolation. + def initialize(isolator, context) + @instance = isolator.subject + @messenger = context.messenger + @messenger.recorder = @recorder = context.recorder + @expectations = context.expectations + @proxy = isolator.isolation + isolator.isolation.class.instance_variable_set("@___context___", self) + end + + # record and send the message to the isolation. + # takes care of following expectations rules when sending messages. + def send_message(method_name, return_type, *args, &b) + @messenger.deliver(method_name, return_type, *args, &b) + end + + # record and send the message to the isolation. + # takes care of following expectations rules when sending messages. + def send_class_message(method_name, return_type, *args, &b) + @messenger.deliver_to_class(method_name, return_type, *args, &b) + end + + # builds up an expectation for an instance method, allows for overriding the result returned by the method + def create_override(method_name, &block) + internal_create_override method_name, :instance, &block + end + + # builds up an expectation for a class method, allows for overriding the result returned by the class method + def create_class_override(method_name, &block) + internal_create_override method_name, :class, &block + end + + # asserts whether the method has been called for the specified configuration + def verify(method_name, &block) + internal_verify method_name, :instance, &block + end + + # asserts whether the method has been called for the specified configuration + def class_verify(method_name, &block) + internal_verify method_name, :class, &block + end + + class << self + + # Creates an isolation object complete with proxy and method call recorder + # It works out which isolation it needs to create and provide and initializes the + # method call recorder + def for(subject, recorder = MethodCallRecorder.new, expectations = Expectations.new) + context = IsolatorContext.new subject, recorder, expectations + + isolator = RubyIsolator.for context + isolation = new(isolator, context) + isolation + end + + end + + protected + + def internal_create_override(method_name, mode=:instance, &block) + builder = ExpectationBuilder.new method_name + block.call builder if block + exp = builder.build + expectations.add_expectation exp, mode + exp + end + + def internal_verify(method_name, mode=:instance, &block) + verification = Verification.new(method_name, recorder, mode) + block.call verification unless block.nil? + verification + end + end + + # +Mock+ is a synonym for +Isolation+ so you can still use it if you're that way inclined + Mock = Isolation unless defined? Mock + + # +Stub+ is a synonym for +Isolation+ so you can still use it if you're that way inclined + Stub = Isolation unless defined? Stub + end \ No newline at end of file