diff --git a/example_specs/passing/subject_example.rb b/example_specs/passing/subject_example.rb new file mode 100644 index 0000000000..3d094a4bda --- /dev/null +++ b/example_specs/passing/subject_example.rb @@ -0,0 +1,45 @@ +require File.dirname(__FILE__) + '/spec_helper' + +module SubjectExample + class OneThing + attr_accessor :what_things_do + def initialize + self.what_things_do = "stuff" + end + end + + # implicit subject + describe OneThing do + it "should do what things do" do + subject.what_things_do.should == "stuff" + end + it "should be a OneThing" do + should == subject + end + its(:what_things_do) { should == "stuff" } + end + + # explicit subject + describe SubjectExample::OneThing do + subject { SubjectExample::OneThing.new } + it "should do what things do" do + subject.what_things_do.should == "stuff" + end + it "should be a OneThing" do + should == subject + end + its(:what_things_do) { should == "stuff" } + end + + # modified subject + describe OneThing do + subject { SubjectExample::OneThing.new } + before { subject.what_things_do = "more stuff" } + it "should do what things do" do + subject.what_things_do.should == "more stuff" + end + its(:what_things_do) { should == "more stuff" } + end + +end + diff --git a/lib/rspec/core/example.rb b/lib/rspec/core/example.rb index 4c60a47b41..618c85a905 100644 --- a/lib/rspec/core/example.rb +++ b/lib/rspec/core/example.rb @@ -35,6 +35,10 @@ def inspect @metadata[:full_description] end + def subject_modifier + @metadata[:subject_modifier] + end + def to_s inspect end diff --git a/lib/rspec/core/example_group.rb b/lib/rspec/core/example_group.rb index 374276a634..32f4f65b9c 100644 --- a/lib/rspec/core/example_group.rb +++ b/lib/rspec/core/example_group.rb @@ -28,6 +28,13 @@ def self.#{name}(desc=nil, options={}, &block) END_RUBY end + def self.its(modifier, desc=nil, options={}, &block) + options.update(:pending => true) unless block + options.update(:caller => caller) + options.update(:subject_modifier => modifier) + examples << Rspec::Core::Example.new(self, desc, options, block) + end + define_example_method :example class << self diff --git a/lib/rspec/core/subject.rb b/lib/rspec/core/subject.rb index e000409f98..5fa77ee407 100644 --- a/lib/rspec/core/subject.rb +++ b/lib/rspec/core/subject.rb @@ -1,17 +1,17 @@ module Rspec module Core module Subject - + def self.included(kls) kls.extend ClassMethods kls.__send__ :alias_method, :__should_for_example_group__, :should kls.__send__ :alias_method, :__should_not_for_example_group__, :should_not end - + def subject - @subject ||= instance_eval(&self.class.subject) + modify_subject? ? modified_subject : unmodified_subject end - + # When +should+ is called with no explicit receiver, the call is # delegated to the object returned by +subject+. Combined with # an implicit subject (see +subject+), this supports very concise @@ -37,7 +37,7 @@ def should(matcher=nil, message=nil) def should_not(matcher=nil, message=nil) self == subject ? self.__should_not_for_example_group__(matcher) : subject.should_not(matcher,message) end - + module ClassMethods # Defines an explicit subject for an example group which can then be the # implicit receiver (through delegation) of calls to +should+. @@ -54,7 +54,7 @@ module ClassMethods def subject(&block) block ? @explicit_subject_block = block : explicit_subject || implicit_subject end - + attr_reader :explicit_subject_block # :nodoc: private @@ -72,6 +72,23 @@ def implicit_subject Class === described ? lambda { described.new } : lambda { described } end end + + private + + def modify_subject? + !running_example.nil? && + !running_example.subject_modifier.nil? && + running_example.state == :block + end + + def unmodified_subject + @unmodified_subject ||= instance_eval(&self.class.subject) + end + + def modified_subject + unmodified_subject.send(running_example.subject_modifier) + end + end end end diff --git a/spec/rspec/core/example_group_spec.rb b/spec/rspec/core/example_group_spec.rb index c5c4e68ea1..c5980468c3 100644 --- a/spec/rspec/core/example_group_spec.rb +++ b/spec/rspec/core/example_group_spec.rb @@ -168,6 +168,12 @@ module Rspec::Core group.examples.size.should == 1 end + it "should allow adding an example using 'its'" do + group = ExampleGroup.create + group.its(:some_method) { } + group.examples.size.should == 1 + end + it "should expose all examples at examples" do group = ExampleGroup.create group.it("should do something 1") { } @@ -374,6 +380,26 @@ def self.count SelfObserver.cache.length.should == 1 end end + + describe "example added with its" do + it "should have a subject_modifier" do + group = ExampleGroup.create + examples = group.its(:some_method) { } + example = examples.last + example.subject_modifier.should === :some_method + end + end + + describe "#its" do + its(:class, "should be ExampleGroup") { should == Rspec::Core::ExampleGroup } + it "does not interfere between tests" do + subject.class.should == Rspec::Core::ExampleGroup + end + context "subject modified in before block" do + before { subject.class.should == Rspec::Core::ExampleGroup } + its(:class, "should be ExampleGroup") { should == Rspec::Core::ExampleGroup } + end + end end end diff --git a/spec/rspec/core/example_spec.rb b/spec/rspec/core/example_spec.rb index 6b4d40786d..7f58a77fd7 100644 --- a/spec/rspec/core/example_spec.rb +++ b/spec/rspec/core/example_spec.rb @@ -29,6 +29,10 @@ @example.should respond_to(:example_block) end + it "should have one for its subject modifier" do + @example.should respond_to(:subject_modifier) + end + it "should have one for its state" do @example.should respond_to(:state) end