lib/shoulda/context/context.rb in shoulda-context-1.2.2 vs lib/shoulda/context/context.rb in shoulda-context-2.0.0.rc1

- old
+ new

@@ -1,292 +1,8 @@ module Shoulda module Context - class << self - def contexts # :nodoc: - @contexts ||= [] - end - attr_writer :contexts - - def current_context # :nodoc: - self.contexts.last - end - - def add_context(context) # :nodoc: - self.contexts.push(context) - end - - def remove_context # :nodoc: - self.contexts.pop - end - 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). - # - # === Example: - # - # class UserTest < Test::Unit::TestCase - # - # def setup - # @user = User.new("John", "Doe") - # end - # - # should "return its full name" - # assert_equal 'John Doe', @user.full_name - # end - # - # end - # - # ...will produce the following test: - # * <tt>"test: User should return its full name. "</tt> - # - # Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class. - # - # Should statements can also take a Proc as a <tt>:before </tt>option. This proc runs after any - # parent context's setups but before the current context's setup. - # - # === Example: - # - # context "Some context" do - # setup { puts("I run after the :before proc") } - # - # 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_or_matcher, options = {}, &blk) - if Shoulda::Context.current_context - Shoulda::Context.current_context.should(name_or_matcher, options, &blk) - else - context_name = self.name.gsub(/Test/, "") if self.name - context = Shoulda::Context::Context.new(context_name, self) do - 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::Context.current_context - Shoulda::Context.current_context.should_not(matcher) - else - context_name = self.name.gsub(/Test/, "") if self.name - context = Shoulda::Context::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. - # - # === Example: - # - # class UserControllerTest < Test::Unit::TestCase - # context "the index action" do - # setup do - # @users = [Factory(:user)] - # User.stubs(:find).returns(@users) - # end - # - # context "on GET" do - # setup { get :index } - # - # should respond_with(:success) - # - # # runs before "get :index" - # before_should "find all users" do - # User.expects(:find).with(:all).returns(@users) - # end - # end - # end - # end - def before_should(name, &blk) - should(name, :before => blk) { assert true } - end - - # Just like should, but never runs, and instead prints an 'X' in the Test::Unit output. - def should_eventually(name, options = {}, &blk) - context_name = self.name.gsub(/Test/, "") - context = Shoulda::Context::Context.new(context_name, self) do - should_eventually(name, &blk) - end - context.build - end - - # == Contexts - # - # A context block groups should statements under a common set of setup/teardown methods. - # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability - # and readability of your test code. - # - # A context block can contain setup, should, should_eventually, and teardown blocks. - # - # class UserTest < Test::Unit::TestCase - # context "A User instance" do - # setup do - # @user = User.find(:first) - # end - # - # should "return its full name" - # assert_equal 'John Doe', @user.full_name - # end - # end - # end - # - # This code will produce the method <tt>"test: A User instance should return its full name. "</tt>. - # - # Contexts may be nested. Nested contexts run their setup blocks from out to in before each - # should statement. They then run their teardown blocks from in to out after each should statement. - # - # class UserTest < Test::Unit::TestCase - # context "A User instance" do - # setup do - # @user = User.find(:first) - # end - # - # should "return its full name" - # assert_equal 'John Doe', @user.full_name - # end - # - # context "with a profile" do - # setup do - # @user.profile = Profile.find(:first) - # end - # - # should "return true when sent :has_profile?" - # assert @user.has_profile? - # end - # end - # end - # end - # - # This code will produce the following methods - # * <tt>"test: A User instance should return its full name. "</tt> - # * <tt>"test: A User instance with a profile should return true when sent :has_profile?. "</tt> - # - # <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt> - # tests</b>. This means you do not have to fully commit to the context/should syntax in a test file. - - def context(name, &blk) - if Shoulda::Context.current_context - Shoulda::Context.current_context.context(name, &blk) - else - context = Shoulda::Context::Context.new(name, self, &blk) - context.build - end - end - - # Returns the class being tested, as determined by the test class name. - # - # class UserTest; described_type; end - # # => User - def described_type - @described_type ||= self.name. - gsub(/Test$/, ''). - split('::'). - inject(Object) do |parent, local_name| - parent.const_get(local_name, false) - end - 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 ||= nil - end - end - - module InstanceMethods - # Returns an instance of the class under test. - # - # class UserTest - # should "be a user" do - # assert_kind_of User, subject # passes - # 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 - # - # The subject is used by all macros that require an instance of the class - # being tested. - def subject - @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) - 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 attr_accessor :parent # may be another context, or the original test::unit class. attr_accessor :subcontexts # array of contexts nested under myself attr_accessor :setup_blocks # blocks given via setup methods attr_accessor :teardown_blocks # blocks given via teardown methods @@ -387,44 +103,44 @@ h[k] = Hash[k.instance_methods.map { |n| [n, true] }] } end def create_test_from_should_hash(should) - test_name = [test_name_prefix, full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym + test_name = build_test_name_from(should) - if test_methods[test_unit_class][test_name.to_s] then - raise DuplicateTestError, "'#{test_name}' is defined more than once." + if test_methods[test_unit_class][test_name.to_s] + raise Shoulda::Context::DuplicateTestError.new( + "'#{test_name}' is defined more than once." + ) end - test_methods[test_unit_class][test_name.to_s] = true - file, line_no = should[:block].source_location + context = self - test_unit_class.class_eval <<-end_eval, file, line_no - define_method test_name do + test_unit_class.__send__(:define_method, test_name) do @shoulda_context = context begin context.run_parent_setup_blocks(self) if should[:before] - if self.respond_to?(:instance_exec) - self.instance_exec(&should[:before]) - else - should[:before].bind(self).call - end + instance_exec(&should[:before]) end context.run_current_setup_blocks(self) - if self.respond_to?(:instance_exec) - self.instance_exec(&should[:block]) - else - should[:block].bind(self).call - end + instance_exec(&should[:block]) ensure context.run_all_teardown_blocks(self) end - end - end_eval + end end + def build_test_name_from(should) + [ + test_name_prefix, + full_name, + "should", + "#{should[:name]}. " + ].flatten.join(' ').to_sym + end + def run_all_setup_blocks(binding) run_parent_setup_blocks(binding) run_current_setup_blocks(binding) end @@ -471,20 +187,21 @@ print_should_eventuallys end def test_name_prefix - if defined?(Minitest) || defined?(MiniTest) + if defined?(Minitest) 'test_:' else 'test:' end end def method_missing(method, *args, &blk) test_unit_class.send(method, *args, &blk) end end + + class DuplicateTestError < RuntimeError; end end end -class DuplicateTestError < RuntimeError; end