lib/shoulda/context.rb in thoughtbot-shoulda-2.10.2 vs lib/shoulda/context.rb in thoughtbot-shoulda-2.11.1

- old
+ new

@@ -19,13 +19,14 @@ end module ClassMethods # == Should statements # - # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block - # contains all the normal code and assertions you're used to seeing, with the added benefit that - # they can be wrapped inside context blocks (see below). + # Should statements are just syntactic sugar over normal Test::Unit test + # methods. A should block contains all the normal code and assertions + # you're used to seeing, with the added benefit that they can be wrapped + # inside context blocks (see below). # # === Example: # # class UserTest < Test::Unit::TestCase # @@ -54,23 +55,52 @@ # # should "run a :before proc", :before => lambda { puts("I run before the setup") } do # assert true # end # end + # + # Should statements can also wrap matchers, making virtually any matcher + # usable in a macro style. The matcher's description is used to generate a + # test name and failure message, and the test will pass if the matcher + # matches the subject. + # + # === Example: + # + # should validate_presence_of(:first_name).with_message(/gotta be there/) + # - def should(name, options = {}, &blk) + def should(name_or_matcher, options = {}, &blk) if Shoulda.current_context - block_given? ? Shoulda.current_context.should(name, options, &blk) : Shoulda.current_context.should_eventually(name) + Shoulda.current_context.should(name_or_matcher, options, &blk) else context_name = self.name.gsub(/Test/, "") context = Shoulda::Context.new(context_name, self) do - block_given? ? should(name, options, &blk) : should_eventually(name) + should(name_or_matcher, options, &blk) end context.build end end + # Allows negative tests using matchers. The matcher's description is used + # to generate a test name and negative failure message, and the test will + # pass unless the matcher matches the subject. + # + # === Example: + # + # should_not set_the_flash + def should_not(matcher) + if Shoulda.current_context + Shoulda.current_context.should_not(matcher) + else + context_name = self.name.gsub(/Test/, "") + context = Shoulda::Context.new(context_name, self) do + should_not(matcher) + end + context.build + end + end + # == Before statements # # Before statements are should statements that run before the current # context's setup. These are especially useful when setting expectations. # @@ -84,11 +114,11 @@ # end # # context "on GET" do # setup { get :index } # - # should_respond_with :success + # should respond_with(:success) # # # runs before "get :index" # before_should "find all users" do # User.expects(:find).with(:all).returns(@users) # end @@ -183,11 +213,11 @@ # # class UserTest < Test::Unit::TestCase # subject { User.first } # # # uses the existing user - # should_validate_uniqueness_of :email + # should validate_uniqueness_of(:email) # end def subject(&block) @subject_block = block end @@ -212,58 +242,41 @@ # should "be an existing user" do # assert !subject.new_record? # uses the first user # end # end # - # If an instance variable exists named after the described class, that - # instance variable will be used as the subject. This behavior is - # deprecated, and will be removed in a future version of Shoulda. The - # recommended approach for using a different subject is to use the subject - # class method. - # - # class UserTest - # should "be the existing user" do - # @user = User.new - # assert_equal @user, subject # passes - # end - # end - # # The subject is used by all macros that require an instance of the class # being tested. def subject - if subject_block - instance_eval(&subject_block) - else - get_instance_of(self.class.described_type) - end + @shoulda_subject ||= construct_subject end def subject_block # :nodoc: (@shoulda_context && @shoulda_context.subject_block) || self.class.subject_block end def get_instance_of(object_or_klass) # :nodoc: if object_or_klass.is_a?(Class) - klass = object_or_klass - ivar = "@#{instance_variable_name_for(klass)}" - if instance = instance_variable_get(ivar) - warn "[WARNING] Using #{ivar} as the subject. Future versions " << - "of Shoulda will require an explicit subject using the " << - "subject class method. Add this after your setup to avoid " << - "this warning: subject { #{ivar} }" - instance - else - klass.new - end + object_or_klass.new else object_or_klass end end def instance_variable_name_for(klass) # :nodoc: klass.to_s.split('::').last.underscore end + + private + + def construct_subject + if subject_block + instance_eval(&subject_block) + else + get_instance_of(self.class.described_type) + end + end end class Context # :nodoc: attr_accessor :name # my name @@ -303,23 +316,41 @@ def teardown(&blk) self.teardown_blocks << blk end - def should(name, options = {}, &blk) - if block_given? + def should(name_or_matcher, options = {}, &blk) + if name_or_matcher.respond_to?(:description) && name_or_matcher.respond_to?(:matches?) + name = name_or_matcher.description + blk = lambda { assert_accepts name_or_matcher, subject } + else + name = name_or_matcher + end + + if blk self.shoulds << { :name => name, :before => options[:before], :block => blk } else self.should_eventuallys << { :name => name } end end + def should_not(matcher) + name = matcher.description + blk = lambda { assert_rejects matcher, subject } + self.shoulds << { :name => "not #{name}", :block => blk } + end + def should_eventually(name, &blk) self.should_eventuallys << { :name => name, :block => blk } end def subject(&block) self.subject_block = block + end + + def subject_block + return @subject_block if @subject_block + parent.subject_block end def full_name parent_name = parent.full_name if am_subcontext? return [parent_name, name].join(" ").strip