-
-
Save kognate/35135 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'spec' | |
class RDingus | |
def initialize(parent = nil, invocation = nil) | |
@parent = parent | |
@invocation = invocation | |
@invocation_exceptions = InvocationList.new | |
@invocation_expectations = InvocationList.new | |
@invocation_expectation_set = InvocationList.new | |
@invocations = [] | |
end | |
def method_missing(name, *args) | |
invocation = InvocationRecord.new(name, args) | |
@invocations << invocation | |
raise @invocation_exceptions[invocation] if @invocation_exceptions[invocation] | |
if @invocation_expectation_set[invocation] | |
@invocation_expectations[invocation] | |
else | |
@invocation_expectation_set[invocation] = true | |
@invocation_expectations[invocation] = RDingus.new(self, invocation) | |
end | |
end | |
def returns(value) | |
@parent.return_value_hook(value, @invocation) | |
end | |
def raises(exception) | |
@parent.raise_value_hook(exception, @invocation) | |
end | |
def calls | |
@invocations | |
end | |
def has_received?(method) | |
@invocations.any? do |invocation| | |
invocation.method == method | |
end | |
end | |
protected | |
def return_value_hook(value, invocation) | |
@invocations.pop | |
@parent.return_value_hook(value, invocation) if @parent | |
@invocation_expectations[invocation] = value | |
end | |
def raise_value_hook(value, invocation) | |
@invocations.pop | |
@parent.raise_value_hook(value, invocation) if @parent | |
@invocation_exceptions[invocation] = value | |
end | |
public | |
class InvocationRecord | |
attr_reader :method, :args | |
attr_accessor :value | |
def initialize(method, args = [], *optional) | |
@method = method | |
@args = args | |
@value = optional.first | |
@has_value = !optional.empty? | |
end | |
def value=(v) | |
@has_value = true | |
@value = v | |
end | |
def has_value? | |
@has_value | |
end | |
def eql?(b) | |
self.method == b.method && ((self.args == b.args) || self.any? || b.any?) | |
end | |
def ==(b) | |
self.eql?(b) | |
end | |
def any? | |
@args && @args.first == :any | |
end | |
def to_s | |
"#{method}(#{args.map{|i|i.inspect}.join(", ")})" | |
end | |
def inspect | |
retval = @has_value ? " => #{@value.inspect}" : "" | |
"#<Invocation \"#{self}\"#{retval}>" | |
end | |
end | |
class InvocationList | |
def initialize | |
@list = [] | |
end | |
def []=(invocation, value) | |
@list << [invocation, value] | |
value | |
end | |
def [](invocation) | |
@list.reverse.each do |inv, value| | |
return value if inv == invocation | |
end | |
nil | |
end | |
end | |
end | |
describe RDingus do | |
before :each do | |
@rdingus = RDingus.new | |
end | |
it "should return an RDingus on method calls" do | |
@rdingus.a_method.should be_kind_of(RDingus) | |
end | |
it "should return the same RDingus for the same method invocation" do | |
@rdingus.a_method.should == @rdingus.a_method | |
end | |
it "should return a different RDingus for a different method" do | |
@rdingus.a_method.should_not == @rdingus.b_method | |
end | |
it "should return a different RDingus for the same method invocation but with different arguments" do | |
@rdingus.a_method.should_not == @rdingus.a_method(:argument) | |
end | |
describe "when specifiying return values" do | |
it "the specification should return the value expected" do | |
@rdingus.a_method.returns(:shell).should == :shell | |
end | |
it "should return what's assigned" do | |
@rdingus.a_method.returns :shell | |
@rdingus.a_method.should == :shell | |
end | |
it "should return false if assigned" do | |
@rdingus.a_method.returns false | |
@rdingus.a_method.should == false | |
end | |
it "should return nil if assigned" do | |
@rdingus.a_method.returns nil | |
@rdingus.a_method.should == nil | |
end | |
it "should not remember as a method call" do | |
@rdingus.a_method.returns "something" | |
@rdingus.calls.should be_empty | |
end | |
it "should not forget other method calls" do | |
@rdingus.remember_me | |
@rdingus.a_method.returns "something" | |
@rdingus.calls.map{|i|i.method}.should == [:remember_me] | |
end | |
describe "of nested method calls" do | |
it "should return what's assigned" do | |
@rdingus.a_method.b_method.returns :stump | |
@rdingus.a_method.b_method.should == :stump | |
end | |
it "should not remember as a method call" do | |
@rdingus.a_method.b_method.returns "something" | |
@rdingus.calls.should be_empty | |
end | |
end | |
describe "with arbitrary arguments" do | |
before :each do | |
@rdingus.a_method(:any).returns :stump | |
end | |
it "should return what's assigned given any arguments" do | |
@rdingus.a_method(:argument).should == :stump | |
end | |
it "should return what's assigned with no arguments" do | |
@rdingus.a_method.should == :stump | |
end | |
end | |
describe "with specific arguments" do | |
before :each do | |
@rdingus.a_method(:argument).returns :stump | |
end | |
it "should return what's assigned" do | |
@rdingus.a_method(:argument).should == :stump | |
end | |
it "should return an RDingus if arguments don't match" do | |
@rdingus.a_method(:argument, :argument2).should be_kind_of(RDingus) | |
end | |
end | |
end | |
describe "when specifying exceptions" do | |
it "should raise given exception" do | |
@rdingus.raise_me.raises "Exception" | |
lambda { @rdingus.raise_me }.should raise_error("Exception") | |
end | |
it "should not remember as a method call" do | |
@rdingus.raise_me.raises "Exception" | |
lambda { @rdingus.raise_me }.should raise_error("Exception") | |
@rdingus.calls.map{|i|i.method}.should == [:raise_me] | |
end | |
it "should not forget other method calls" do | |
@rdingus.remember_me | |
@rdingus.raise_me.raises "Exception" | |
lambda { @rdingus.raise_me }.should raise_error("Exception") | |
@rdingus.calls.map{|i|i.method}.should == [:remember_me, :raise_me] | |
end | |
end | |
describe "recording invocations" do | |
it "should remember methods invoked" do | |
@rdingus.sandwich | |
@rdingus.calls.first.method.should == :sandwich | |
end | |
it "should remember with arguments invoked" do | |
@rdingus.sandwich(:with_cheese) | |
@rdingus.calls.first.args.first.should == :with_cheese | |
end | |
end | |
end | |
describe RDingus::InvocationRecord do | |
before :each do | |
@invocation_record = RDingus::InvocationRecord.new(:method, [:arg1, :arg2]) | |
end | |
it "should be equal for same method and arguments" do | |
RDingus::InvocationRecord.new(:method, [:arg1, :arg2]).should == @invocation_record | |
end | |
it "should not be equal for different method" do | |
RDingus::InvocationRecord.new(:method2, [:arg1, :arg2]).should_not == @invocation_record | |
end | |
it "should not be equal for different arguments" do | |
RDingus::InvocationRecord.new(:method, [:arg2, :arg3]).should_not == @invocation_record | |
end | |
it "should define == the same as eql?" do | |
RDingus::InvocationRecord.new(:method, [:arg1, :arg2]).should be_eql(@invocation_record) | |
end | |
describe "when told to accept any arguments" do | |
before :each do | |
@invocation_record = RDingus::InvocationRecord.new(:method, [:any]) | |
end | |
it "should be equal for same method and same arguments" do | |
RDingus::InvocationRecord.new(:method, [:any]).should == @invocation_record | |
end | |
it "should be equal for same method and different arguments" do | |
RDingus::InvocationRecord.new(:method, [:other, :args]).should == @invocation_record | |
end | |
it "should not be equal for different method" do | |
RDingus::InvocationRecord.new(:method2, [:arg1, :arg2]).should_not == @invocation_record | |
end | |
end | |
it "should convert to a useful string" do | |
@invocation_record.to_s.should == "method(:arg1, :arg2)" | |
end | |
it "should have a useful inspect result" do | |
@invocation_record.value = :arg3 | |
@invocation_record.inspect.should == "#<Invocation \"method(:arg1, :arg2)\" => :arg3>" | |
end | |
end | |
describe RDingus::InvocationList do | |
before :each do | |
@invocation = RDingus::InvocationRecord.new(:method, [:arg1, :arg2]) | |
@invocation_list = RDingus::InvocationList.new | |
end | |
it "should store and retrieve for same invocation" do | |
@invocation_list[@invocation] = "test" | |
@invocation_list[@invocation].should == "test" | |
end | |
it "should store and retrieve for equivalent invocations" do | |
@invocation_list[RDingus::InvocationRecord.new(:method, [:any])] = 'test' | |
@invocation_list[@invocation].should == "test" | |
end | |
it "should not store and retrieve for non equivalent invocations" do | |
@invocation_list[RDingus::InvocationRecord.new(:method2)] = "test" | |
@invocation_list[@invocation].should_not == "test" | |
end | |
describe "when redefining invocation" do | |
it "should use the last one" do | |
@invocation_list[@invocation] = "first" | |
@invocation_list[RDingus::InvocationRecord.new(:method, [:any])] = "second" | |
@invocation_list[@invocation].should == "second" | |
end | |
end | |
end | |
class Person | |
attr_accessor :name | |
attr_accessor :shoe_size | |
def eat(sandwich) | |
@sandwich = sandwich | |
@sandwich.cut :in => 2 | |
@sandwich.take_a_bite | |
end | |
def drop(sandwich) | |
sandwich.drop | |
end | |
def opinion_of_sandwich | |
if @sandwich.cheese.spicy? | |
"yummy!" | |
else | |
"bland" | |
end | |
end | |
end | |
describe Person do | |
before :each do | |
@person = Person.new | |
end | |
describe "when specifying a shoe size" do | |
it "should save the shoe size" do | |
@person.shoe_size = 4 | |
@person.shoe_size.should == 4 | |
end | |
end | |
describe "when using a sandwich" do | |
before :each do | |
@sandwich = RDingus.new | |
@person.eat(@sandwich) | |
end | |
it "should be able to cut a sandwhich" do | |
@sandwich.calls.first.method.should == :cut | |
end | |
it "should cut into two slices" do | |
@sandwich.calls[0].args[0].should == {:in => 2} | |
end | |
it "should take a bite" do | |
@sandwich.should have_received(:take_a_bite) | |
end | |
it "should not have been dropped" do | |
@sandwich.should_not have_received(:dropped) | |
end | |
it "should raise an exception if it drops the sandwich" do | |
@sandwich.drop.raises "Dropped Sandwich!" | |
lambda { @person.drop(@sandwich) }.should raise_error("Dropped Sandwich!") | |
end | |
describe "that has spicy cheese" do | |
before :each do | |
@sandwich.cheese.spicy?.returns true | |
end | |
it "should have an opinion of 'yummy!'" do | |
@person.opinion_of_sandwich.should == "yummy!" | |
end | |
end | |
describe "that does not have spicy cheese" do | |
before :each do | |
@sandwich.cheese.spicy?.returns false | |
end | |
it "should have an opinion of 'bland'" do | |
@person.opinion_of_sandwich.should == "bland" | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment