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