Permalink
Browse files

Added the subject class method to explicitly declare the subject [#86]

  • Loading branch information...
1 parent 488a3db commit 7e17dec52c674266e239ce0555f0f2cc149625b5 @jferris jferris committed May 7, 2009
Showing with 66 additions and 38 deletions.
  1. +0 −36 lib/shoulda/active_record/macros.rb
  2. +43 −2 lib/shoulda/context.rb
  3. +23 −0 test/other/context_test.rb
@@ -25,10 +25,6 @@ module Macros
# Ensures that the model cannot be saved if one of the attributes listed is not present.
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.blank')</tt>
@@ -127,10 +123,6 @@ def should_have_readonly_attributes(*attributes)
# Ensures that the attribute cannot be set to the given values
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.invalid')</tt>
@@ -150,10 +142,6 @@ def should_not_allow_values_for(attribute, *bad_values)
# Ensures that the attribute can be set to the given values.
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Example:
# should_allow_values_for :isbn, "isbn 1 2345 6789 0", "ISBN 1-2345-6789-0"
#
@@ -169,10 +157,6 @@ def should_allow_values_for(attribute, *good_values)
# Ensures that the length of the attribute is in the given range
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % range.first</tt>
@@ -199,10 +183,6 @@ def should_ensure_length_in_range(attribute, range, opts = {})
# Ensures that the length of the attribute is at least a certain length
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:short_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.too_short') % min_length</tt>
@@ -224,10 +204,6 @@ def should_ensure_length_at_least(attribute, min_length, opts = {})
# Ensures that the length of the attribute is exactly a certain length
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.wrong_length') % length</tt>
@@ -248,10 +224,6 @@ def should_ensure_length_is(attribute, length, opts = {})
# Ensure that the attribute is in the range specified
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:low_message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.inclusion')</tt>
@@ -278,10 +250,6 @@ def should_ensure_value_in_range(attribute, range, opts = {})
# Ensure that the attribute is numeric
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
@@ -477,10 +445,6 @@ def should_have_indices(*args)
# Ensures that the model cannot be saved if one of the attributes listed is not accepted.
#
- # If an instance variable has been created in the setup named after the
- # model being tested, then this method will use that. Otherwise, it will
- # create a new instance to test against.
- #
# Options:
# * <tt>:message</tt> - value the test expects to find in <tt>errors.on(:attribute)</tt>.
# Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
View
@@ -178,6 +178,22 @@ def context(name, &blk)
def described_type
self.name.gsub(/Test$/, '').constantize
end
+
+ # Sets the return value of the subject instance method:
+ #
+ # class UserTest < Test::Unit::TestCase
+ # subject { User.first }
+ #
+ # # uses the existing user
+ # should_validate_uniqueness_of :email
+ # end
+ def subject(&block)
+ @subject_block = block
+ end
+
+ def subject_block # :nodoc:
+ @subject_block
+ end
end
module InstanceMethods
@@ -189,6 +205,15 @@ module InstanceMethods
# end
# end
#
+ # The subject can be explicitly set using the subject class method:
+ #
+ # class UserTest
+ # subject { User.first }
+ # 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.
#
@@ -198,8 +223,19 @@ module InstanceMethods
# 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
- get_instance_of(self.class.described_type)
+ if subject_block
+ instance_eval(&subject_block)
+ else
+ get_instance_of(self.class.described_type)
+ end
+ end
+
+ def subject_block # :nodoc:
+ (@shoulda_context && @shoulda_context.subject_block) || self.class.subject_block
end
def get_instance_of(object_or_klass) # :nodoc:
@@ -214,7 +250,6 @@ def get_instance_of(object_or_klass) # :nodoc:
def instance_variable_name_for(klass) # :nodoc:
klass.to_s.split('::').last.underscore
end
-
end
class Context # :nodoc:
@@ -226,6 +261,7 @@ class Context # :nodoc:
attr_accessor :teardown_blocks # blocks given via teardown methods
attr_accessor :shoulds # array of hashes representing the should statements
attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
+ attr_accessor :subject_block
def initialize(name, parent, &blk)
Shoulda.add_context(self)
@@ -269,6 +305,10 @@ def should_eventually(name, &blk)
self.should_eventuallys << { :name => name, :block => blk }
end
+ def subject(&block)
+ self.subject_block = block
+ end
+
def full_name
parent_name = parent.full_name if am_subcontext?
return [parent_name, name].join(" ").strip
@@ -291,6 +331,7 @@ def create_test_from_should_hash(should)
context = self
test_unit_class.send(:define_method, test_name) do
+ @shoulda_context = context
begin
context.run_parent_setup_blocks(self)
should[:before].bind(self).call if should[:before]
View
@@ -162,5 +162,28 @@ class ::SomeModel; end
@some_model = SomeModel.new
assert_equal @some_model, subject
end
+
+ context "with an explicit subject block" do
+ setup { @expected = SomeModel.new }
+ subject { @expected }
+ should "return the result of the block as the subject" do
+ assert_equal @expected, subject
+ end
+ end
+ end
+end
+
+class Subject; end
+
+class SubjectTest < ActiveSupport::TestCase
+
+ def setup
+ @expected = Subject.new
+ end
+
+ subject { @expected }
+
+ should "return a specified subject" do
+ assert_equal @expected, subject
end
end

0 comments on commit 7e17dec

Please sign in to comment.