iGEL-shoulda 2.10.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
  2. data/MIT-LICENSE +22 -0
  3. data/README.rdoc +171 -0
  4. data/Rakefile +72 -0
  5. data/bin/convert_to_should_syntax +42 -0
  6. data/lib/shoulda.rb +9 -0
  7. data/lib/shoulda/action_controller.rb +26 -0
  8. data/lib/shoulda/action_controller/macros.rb +240 -0
  9. data/lib/shoulda/action_controller/matchers.rb +37 -0
  10. data/lib/shoulda/action_controller/matchers/assign_to_matcher.rb +109 -0
  11. data/lib/shoulda/action_controller/matchers/filter_param_matcher.rb +57 -0
  12. data/lib/shoulda/action_controller/matchers/render_with_layout_matcher.rb +81 -0
  13. data/lib/shoulda/action_controller/matchers/respond_with_content_type_matcher.rb +74 -0
  14. data/lib/shoulda/action_controller/matchers/respond_with_matcher.rb +81 -0
  15. data/lib/shoulda/action_controller/matchers/route_matcher.rb +93 -0
  16. data/lib/shoulda/action_controller/matchers/set_session_matcher.rb +87 -0
  17. data/lib/shoulda/action_controller/matchers/set_the_flash_matcher.rb +85 -0
  18. data/lib/shoulda/action_mailer.rb +10 -0
  19. data/lib/shoulda/action_mailer/assertions.rb +38 -0
  20. data/lib/shoulda/action_view.rb +10 -0
  21. data/lib/shoulda/action_view/macros.rb +61 -0
  22. data/lib/shoulda/active_record.rb +16 -0
  23. data/lib/shoulda/active_record/assertions.rb +69 -0
  24. data/lib/shoulda/active_record/helpers.rb +27 -0
  25. data/lib/shoulda/active_record/macros.rb +512 -0
  26. data/lib/shoulda/active_record/matchers.rb +43 -0
  27. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  28. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  29. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  30. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  31. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  32. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  33. data/lib/shoulda/active_record/matchers/have_db_index_matcher.rb +112 -0
  34. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +128 -0
  35. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  36. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  37. data/lib/shoulda/active_record/matchers/validate_format_of_matcher.rb +67 -0
  38. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  39. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  40. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  41. data/lib/shoulda/active_record/matchers/validation_matcher.rb +57 -0
  42. data/lib/shoulda/assertions.rb +71 -0
  43. data/lib/shoulda/autoload_macros.rb +46 -0
  44. data/lib/shoulda/context.rb +402 -0
  45. data/lib/shoulda/helpers.rb +8 -0
  46. data/lib/shoulda/macros.rb +133 -0
  47. data/lib/shoulda/private_helpers.rb +13 -0
  48. data/lib/shoulda/proc_extensions.rb +14 -0
  49. data/lib/shoulda/rails.rb +13 -0
  50. data/lib/shoulda/rspec.rb +11 -0
  51. data/lib/shoulda/tasks.rb +3 -0
  52. data/lib/shoulda/tasks/list_tests.rake +29 -0
  53. data/lib/shoulda/tasks/yaml_to_shoulda.rake +28 -0
  54. data/lib/shoulda/test_unit.rb +22 -0
  55. data/rails/init.rb +7 -0
  56. data/test/README +36 -0
  57. data/test/fail_macros.rb +39 -0
  58. data/test/fixtures/addresses.yml +3 -0
  59. data/test/fixtures/friendships.yml +0 -0
  60. data/test/fixtures/posts.yml +5 -0
  61. data/test/fixtures/products.yml +0 -0
  62. data/test/fixtures/taggings.yml +0 -0
  63. data/test/fixtures/tags.yml +9 -0
  64. data/test/fixtures/users.yml +6 -0
  65. data/test/functional/posts_controller_test.rb +124 -0
  66. data/test/functional/users_controller_test.rb +19 -0
  67. data/test/matchers/active_record/allow_mass_assignment_of_matcher_test.rb +68 -0
  68. data/test/matchers/active_record/allow_value_matcher_test.rb +64 -0
  69. data/test/matchers/active_record/association_matcher_test.rb +263 -0
  70. data/test/matchers/active_record/ensure_inclusion_of_matcher_test.rb +80 -0
  71. data/test/matchers/active_record/ensure_length_of_matcher_test.rb +158 -0
  72. data/test/matchers/active_record/have_db_column_matcher_test.rb +169 -0
  73. data/test/matchers/active_record/have_db_index_matcher_test.rb +91 -0
  74. data/test/matchers/active_record/have_named_scope_matcher_test.rb +65 -0
  75. data/test/matchers/active_record/have_readonly_attributes_matcher_test.rb +29 -0
  76. data/test/matchers/active_record/validate_acceptance_of_matcher_test.rb +44 -0
  77. data/test/matchers/active_record/validate_format_of_matcher_test.rb +39 -0
  78. data/test/matchers/active_record/validate_numericality_of_matcher_test.rb +52 -0
  79. data/test/matchers/active_record/validate_presence_of_matcher_test.rb +157 -0
  80. data/test/matchers/active_record/validate_uniqueness_of_matcher_test.rb +147 -0
  81. data/test/matchers/controller/assign_to_matcher_test.rb +38 -0
  82. data/test/matchers/controller/filter_param_matcher_test.rb +32 -0
  83. data/test/matchers/controller/render_with_layout_matcher_test.rb +33 -0
  84. data/test/matchers/controller/respond_with_content_type_matcher_test.rb +32 -0
  85. data/test/matchers/controller/respond_with_matcher_test.rb +106 -0
  86. data/test/matchers/controller/route_matcher_test.rb +58 -0
  87. data/test/matchers/controller/set_session_matcher_test.rb +38 -0
  88. data/test/matchers/controller/set_the_flash_matcher.rb +41 -0
  89. data/test/model_builder.rb +106 -0
  90. data/test/other/autoload_macro_test.rb +18 -0
  91. data/test/other/context_test.rb +189 -0
  92. data/test/other/convert_to_should_syntax_test.rb +63 -0
  93. data/test/other/helpers_test.rb +340 -0
  94. data/test/other/private_helpers_test.rb +32 -0
  95. data/test/other/should_test.rb +271 -0
  96. data/test/rails_root/app/controllers/application_controller.rb +25 -0
  97. data/test/rails_root/app/controllers/posts_controller.rb +87 -0
  98. data/test/rails_root/app/controllers/users_controller.rb +84 -0
  99. data/test/rails_root/app/helpers/application_helper.rb +3 -0
  100. data/test/rails_root/app/helpers/posts_helper.rb +2 -0
  101. data/test/rails_root/app/helpers/users_helper.rb +2 -0
  102. data/test/rails_root/app/models/address.rb +7 -0
  103. data/test/rails_root/app/models/flea.rb +3 -0
  104. data/test/rails_root/app/models/friendship.rb +4 -0
  105. data/test/rails_root/app/models/pets/cat.rb +7 -0
  106. data/test/rails_root/app/models/pets/dog.rb +10 -0
  107. data/test/rails_root/app/models/post.rb +12 -0
  108. data/test/rails_root/app/models/product.rb +12 -0
  109. data/test/rails_root/app/models/profile.rb +2 -0
  110. data/test/rails_root/app/models/registration.rb +2 -0
  111. data/test/rails_root/app/models/tag.rb +8 -0
  112. data/test/rails_root/app/models/tagging.rb +4 -0
  113. data/test/rails_root/app/models/treat.rb +3 -0
  114. data/test/rails_root/app/models/user.rb +32 -0
  115. data/test/rails_root/app/views/layouts/posts.rhtml +19 -0
  116. data/test/rails_root/app/views/layouts/users.rhtml +17 -0
  117. data/test/rails_root/app/views/layouts/wide.html.erb +1 -0
  118. data/test/rails_root/app/views/posts/edit.rhtml +27 -0
  119. data/test/rails_root/app/views/posts/index.rhtml +25 -0
  120. data/test/rails_root/app/views/posts/new.rhtml +26 -0
  121. data/test/rails_root/app/views/posts/show.rhtml +18 -0
  122. data/test/rails_root/app/views/users/edit.rhtml +22 -0
  123. data/test/rails_root/app/views/users/index.rhtml +22 -0
  124. data/test/rails_root/app/views/users/new.rhtml +21 -0
  125. data/test/rails_root/app/views/users/show.rhtml +13 -0
  126. data/test/rails_root/config/boot.rb +110 -0
  127. data/test/rails_root/config/database.yml +4 -0
  128. data/test/rails_root/config/environment.rb +18 -0
  129. data/test/rails_root/config/environments/test.rb +0 -0
  130. data/test/rails_root/config/initializers/new_rails_defaults.rb +15 -0
  131. data/test/rails_root/config/initializers/shoulda.rb +8 -0
  132. data/test/rails_root/config/routes.rb +6 -0
  133. data/test/rails_root/db/migrate/001_create_users.rb +19 -0
  134. data/test/rails_root/db/migrate/002_create_posts.rb +13 -0
  135. data/test/rails_root/db/migrate/003_create_taggings.rb +12 -0
  136. data/test/rails_root/db/migrate/004_create_tags.rb +11 -0
  137. data/test/rails_root/db/migrate/005_create_dogs.rb +12 -0
  138. data/test/rails_root/db/migrate/006_create_addresses.rb +14 -0
  139. data/test/rails_root/db/migrate/007_create_fleas.rb +11 -0
  140. data/test/rails_root/db/migrate/008_create_dogs_fleas.rb +12 -0
  141. data/test/rails_root/db/migrate/009_create_products.rb +17 -0
  142. data/test/rails_root/db/migrate/010_create_friendships.rb +14 -0
  143. data/test/rails_root/db/migrate/011_create_treats.rb +12 -0
  144. data/test/rails_root/db/migrate/20090506203502_create_profiles.rb +12 -0
  145. data/test/rails_root/db/migrate/20090506203536_create_registrations.rb +14 -0
  146. data/test/rails_root/db/migrate/20090513104502_create_cats.rb +12 -0
  147. data/test/rails_root/db/schema.rb +0 -0
  148. data/test/rails_root/public/404.html +30 -0
  149. data/test/rails_root/public/422.html +30 -0
  150. data/test/rails_root/public/500.html +30 -0
  151. data/test/rails_root/script/console +3 -0
  152. data/test/rails_root/script/generate +3 -0
  153. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  154. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  155. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  156. data/test/rspec_test.rb +207 -0
  157. data/test/test_helper.rb +28 -0
  158. data/test/unit/address_test.rb +10 -0
  159. data/test/unit/cat_test.rb +7 -0
  160. data/test/unit/dog_test.rb +9 -0
  161. data/test/unit/flea_test.rb +6 -0
  162. data/test/unit/friendship_test.rb +6 -0
  163. data/test/unit/post_test.rb +19 -0
  164. data/test/unit/product_test.rb +23 -0
  165. data/test/unit/tag_test.rb +15 -0
  166. data/test/unit/tagging_test.rb +6 -0
  167. data/test/unit/user_test.rb +80 -0
  168. metadata +264 -0
@@ -0,0 +1,57 @@
1
+ module Shoulda # :nodoc:
2
+ module ActiveRecord # :nodoc:
3
+ module Matchers
4
+
5
+ class ValidationMatcher # :nodoc:
6
+
7
+ attr_reader :failure_message
8
+
9
+ def initialize(attribute)
10
+ @attribute = attribute
11
+ end
12
+
13
+ def negative_failure_message
14
+ @negative_failure_message || @failure_message
15
+ end
16
+
17
+ def matches?(subject)
18
+ @subject = subject
19
+ false
20
+ end
21
+
22
+
23
+ private
24
+
25
+ def allows_value_of(value, message = nil)
26
+ allow = AllowValueMatcher.
27
+ new(value).
28
+ for(@attribute).
29
+ with_message(message)
30
+ if allow.matches?(@subject)
31
+ @negative_failure_message = allow.failure_message
32
+ true
33
+ else
34
+ @failure_message = allow.negative_failure_message
35
+ false
36
+ end
37
+ end
38
+
39
+ def disallows_value_of(value, message = nil)
40
+ disallow = AllowValueMatcher.
41
+ new(value).
42
+ for(@attribute).
43
+ with_message(message)
44
+ if disallow.matches?(@subject)
45
+ @failure_message = disallow.negative_failure_message
46
+ false
47
+ else
48
+ @negative_failure_message = disallow.failure_message
49
+ true
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,71 @@
1
+ module Shoulda # :nodoc:
2
+ module Assertions
3
+ # Asserts that two arrays contain the same elements, the same number of times. Essentially ==, but unordered.
4
+ #
5
+ # assert_same_elements([:a, :b, :c], [:c, :a, :b]) => passes
6
+ def assert_same_elements(a1, a2, msg = nil)
7
+ [:select, :inject, :size].each do |m|
8
+ [a1, a2].each {|a| assert_respond_to(a, m, "Are you sure that #{a.inspect} is an array? It doesn't respond to #{m}.") }
9
+ end
10
+
11
+ assert a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
12
+ assert a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
13
+
14
+ assert_equal(a1h, a2h, msg)
15
+ end
16
+
17
+ # Asserts that the given collection contains item x. If x is a regular expression, ensure that
18
+ # at least one element from the collection matches x. +extra_msg+ is appended to the error message if the assertion fails.
19
+ #
20
+ # assert_contains(['a', '1'], /\d/) => passes
21
+ # assert_contains(['a', '1'], 'a') => passes
22
+ # assert_contains(['a', '1'], /not there/) => fails
23
+ def assert_contains(collection, x, extra_msg = "")
24
+ collection = [collection] unless collection.is_a?(Array)
25
+ msg = "#{x.inspect} not found in #{collection.to_a.inspect} #{extra_msg}"
26
+ case x
27
+ when Regexp
28
+ assert(collection.detect { |e| e =~ x }, msg)
29
+ else
30
+ assert(collection.include?(x), msg)
31
+ end
32
+ end
33
+
34
+ # Asserts that the given collection does not contain item x. If x is a regular expression, ensure that
35
+ # none of the elements from the collection match x.
36
+ def assert_does_not_contain(collection, x, extra_msg = "")
37
+ collection = [collection] unless collection.is_a?(Array)
38
+ msg = "#{x.inspect} found in #{collection.to_a.inspect} " + extra_msg
39
+ case x
40
+ when Regexp
41
+ assert(!collection.detect { |e| e =~ x }, msg)
42
+ else
43
+ assert(!collection.include?(x), msg)
44
+ end
45
+ end
46
+
47
+ # Asserts that the given matcher returns true when +target+ is passed to #matches?
48
+ def assert_accepts(matcher, target, options = {})
49
+ if matcher.matches?(target)
50
+ assert_block { true }
51
+ if options[:message]
52
+ assert_match options[:message], matcher.negative_failure_message
53
+ end
54
+ else
55
+ assert_block(matcher.failure_message) { false }
56
+ end
57
+ end
58
+
59
+ # Asserts that the given matcher returns false when +target+ is passed to #matches?
60
+ def assert_rejects(matcher, target, options = {})
61
+ unless matcher.matches?(target)
62
+ assert_block { true }
63
+ if options[:message]
64
+ assert_match options[:message], matcher.failure_message
65
+ end
66
+ else
67
+ assert_block(matcher.negative_failure_message) { false }
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,46 @@
1
+ module Shoulda # :nodoc:
2
+ # Call autoload_macros when you want to load test macros automatically in a non-Rails
3
+ # project (it's done automatically for Rails projects).
4
+ # You don't need to specify ROOT/test/shoulda_macros explicitly. Your custom macros
5
+ # are loaded automatically when you call autoload_macros.
6
+ #
7
+ # The first argument is the path to you application's root directory.
8
+ # All following arguments are directories relative to your root, which contain
9
+ # shoulda_macros subdirectories. These directories support the same kinds of globs as the
10
+ # Dir class.
11
+ #
12
+ # Basic usage (from a test_helper):
13
+ # Shoulda.autoload_macros(File.dirname(__FILE__) + '/..')
14
+ # will load everything in
15
+ # - your_app/test/shoulda_macros
16
+ #
17
+ # To load vendored macros as well:
18
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/*')
19
+ # will load everything in
20
+ # - APP_ROOT/vendor/*/shoulda_macros
21
+ # - APP_ROOT/test/shoulda_macros
22
+ #
23
+ # To load macros in an app with a vendor directory laid out like Rails':
24
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/{plugins,gems}/*')
25
+ # or
26
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/plugins/*', 'vendor/gems/*')
27
+ # will load everything in
28
+ # - APP_ROOT/vendor/plugins/*/shoulda_macros
29
+ # - APP_ROOT/vendor/gems/*/shoulda_macros
30
+ # - APP_ROOT/test/shoulda_macros
31
+ #
32
+ # If you prefer to stick testing dependencies away from your production dependencies:
33
+ # Shoulda.autoload_macros(APP_ROOT, 'vendor/*', 'test/vendor/*')
34
+ # will load everything in
35
+ # - APP_ROOT/vendor/*/shoulda_macros
36
+ # - APP_ROOT/test/vendor/*/shoulda_macros
37
+ # - APP_ROOT/test/shoulda_macros
38
+ def self.autoload_macros(root, *dirs)
39
+ dirs << File.join('test')
40
+ complete_dirs = dirs.map{|d| File.join(root, d, 'shoulda_macros')}
41
+ all_files = complete_dirs.inject([]){ |files, dir| files + Dir[File.join(dir, '*.rb')] }
42
+ all_files.each do |file|
43
+ require file
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,402 @@
1
+ module Shoulda
2
+ class << self
3
+ attr_accessor :contexts
4
+ def contexts # :nodoc:
5
+ @contexts ||= []
6
+ end
7
+
8
+ def current_context # :nodoc:
9
+ self.contexts.last
10
+ end
11
+
12
+ def add_context(context) # :nodoc:
13
+ self.contexts.push(context)
14
+ end
15
+
16
+ def remove_context # :nodoc:
17
+ self.contexts.pop
18
+ end
19
+ end
20
+
21
+ module ClassMethods
22
+ # == Should statements
23
+ #
24
+ # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
25
+ # contains all the normal code and assertions you're used to seeing, with the added benefit that
26
+ # they can be wrapped inside context blocks (see below).
27
+ #
28
+ # === Example:
29
+ #
30
+ # class UserTest < Test::Unit::TestCase
31
+ #
32
+ # def setup
33
+ # @user = User.new("John", "Doe")
34
+ # end
35
+ #
36
+ # should "return its full name"
37
+ # assert_equal 'John Doe', @user.full_name
38
+ # end
39
+ #
40
+ # end
41
+ #
42
+ # ...will produce the following test:
43
+ # * <tt>"test: User should return its full name. "</tt>
44
+ #
45
+ # Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
46
+ #
47
+ # Should statements can also take a Proc as a <tt>:before </tt>option. This proc runs after any
48
+ # parent context's setups but before the current context's setup.
49
+ #
50
+ # === Example:
51
+ #
52
+ # context "Some context" do
53
+ # setup { puts("I run after the :before proc") }
54
+ #
55
+ # should "run a :before proc", :before => lambda { puts("I run before the setup") } do
56
+ # assert true
57
+ # end
58
+ # end
59
+
60
+ def should(name, options = {}, &blk)
61
+ if Shoulda.current_context
62
+ block_given? ? Shoulda.current_context.should(name, options, &blk) : Shoulda.current_context.should_eventually(name)
63
+ else
64
+ context_name = self.name.gsub(/Test/, "")
65
+ context = Shoulda::Context.new(context_name, self) do
66
+ block_given? ? should(name, options, &blk) : should_eventually(name)
67
+ end
68
+ context.build
69
+ end
70
+ end
71
+
72
+ # == Before statements
73
+ #
74
+ # Before statements are should statements that run before the current
75
+ # context's setup. These are especially useful when setting expectations.
76
+ #
77
+ # === Example:
78
+ #
79
+ # class UserControllerTest < Test::Unit::TestCase
80
+ # context "the index action" do
81
+ # setup do
82
+ # @users = [Factory(:user)]
83
+ # User.stubs(:find).returns(@users)
84
+ # end
85
+ #
86
+ # context "on GET" do
87
+ # setup { get :index }
88
+ #
89
+ # should_respond_with :success
90
+ #
91
+ # # runs before "get :index"
92
+ # before_should "find all users" do
93
+ # User.expects(:find).with(:all).returns(@users)
94
+ # end
95
+ # end
96
+ # end
97
+ # end
98
+ def before_should(name, &blk)
99
+ should(name, :before => blk) { assert true }
100
+ end
101
+
102
+ # Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
103
+ def should_eventually(name, options = {}, &blk)
104
+ context_name = self.name.gsub(/Test/, "")
105
+ context = Shoulda::Context.new(context_name, self) do
106
+ should_eventually(name, &blk)
107
+ end
108
+ context.build
109
+ end
110
+
111
+ # == Contexts
112
+ #
113
+ # A context block groups should statements under a common set of setup/teardown methods.
114
+ # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
115
+ # and readability of your test code.
116
+ #
117
+ # A context block can contain setup, should, should_eventually, and teardown blocks.
118
+ #
119
+ # class UserTest < Test::Unit::TestCase
120
+ # context "A User instance" do
121
+ # setup do
122
+ # @user = User.find(:first)
123
+ # end
124
+ #
125
+ # should "return its full name"
126
+ # assert_equal 'John Doe', @user.full_name
127
+ # end
128
+ # end
129
+ # end
130
+ #
131
+ # This code will produce the method <tt>"test: A User instance should return its full name. "</tt>.
132
+ #
133
+ # Contexts may be nested. Nested contexts run their setup blocks from out to in before each
134
+ # should statement. They then run their teardown blocks from in to out after each should statement.
135
+ #
136
+ # class UserTest < Test::Unit::TestCase
137
+ # context "A User instance" do
138
+ # setup do
139
+ # @user = User.find(:first)
140
+ # end
141
+ #
142
+ # should "return its full name"
143
+ # assert_equal 'John Doe', @user.full_name
144
+ # end
145
+ #
146
+ # context "with a profile" do
147
+ # setup do
148
+ # @user.profile = Profile.find(:first)
149
+ # end
150
+ #
151
+ # should "return true when sent :has_profile?"
152
+ # assert @user.has_profile?
153
+ # end
154
+ # end
155
+ # end
156
+ # end
157
+ #
158
+ # This code will produce the following methods
159
+ # * <tt>"test: A User instance should return its full name. "</tt>
160
+ # * <tt>"test: A User instance with a profile should return true when sent :has_profile?. "</tt>
161
+ #
162
+ # <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
163
+ # tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
164
+
165
+ def context(name, &blk)
166
+ if Shoulda.current_context
167
+ Shoulda.current_context.context(name, &blk)
168
+ else
169
+ context = Shoulda::Context.new(name, self, &blk)
170
+ context.build
171
+ end
172
+ end
173
+
174
+ # Returns the class being tested, as determined by the test class name.
175
+ #
176
+ # class UserTest; described_type; end
177
+ # # => User
178
+ def described_type
179
+ self.name.gsub(/Test$/, '').constantize
180
+ end
181
+
182
+ # Sets the return value of the subject instance method:
183
+ #
184
+ # class UserTest < Test::Unit::TestCase
185
+ # subject { User.first }
186
+ #
187
+ # # uses the existing user
188
+ # should_validate_uniqueness_of :email
189
+ # end
190
+ def subject(&block)
191
+ @subject_block = block
192
+ end
193
+
194
+ def subject_block # :nodoc:
195
+ @subject_block
196
+ end
197
+ end
198
+
199
+ module InstanceMethods
200
+ # Returns an instance of the class under test.
201
+ #
202
+ # class UserTest
203
+ # should "be a user" do
204
+ # assert_kind_of User, subject # passes
205
+ # end
206
+ # end
207
+ #
208
+ # The subject can be explicitly set using the subject class method:
209
+ #
210
+ # class UserTest
211
+ # subject { User.first }
212
+ # should "be an existing user" do
213
+ # assert !subject.new_record? # uses the first user
214
+ # end
215
+ # end
216
+ #
217
+ # If an instance variable exists named after the described class, that
218
+ # instance variable will be used as the subject. This behavior is
219
+ # deprecated, and will be removed in a future version of Shoulda. The
220
+ # recommended approach for using a different subject is to use the subject
221
+ # class method.
222
+ #
223
+ # class UserTest
224
+ # should "be the existing user" do
225
+ # @user = User.new
226
+ # assert_equal @user, subject # passes
227
+ # end
228
+ # end
229
+ #
230
+ # The subject is used by all macros that require an instance of the class
231
+ # being tested.
232
+ def subject
233
+ if subject_block
234
+ instance_eval(&subject_block)
235
+ else
236
+ get_instance_of(self.class.described_type)
237
+ end
238
+ end
239
+
240
+ def subject_block # :nodoc:
241
+ (@shoulda_context && @shoulda_context.subject_block) || self.class.subject_block
242
+ end
243
+
244
+ def get_instance_of(object_or_klass) # :nodoc:
245
+ if object_or_klass.is_a?(Class)
246
+ klass = object_or_klass
247
+ ivar = "@#{instance_variable_name_for(klass)}"
248
+ if instance = instance_variable_get(ivar)
249
+ warn "[WARNING] Using #{ivar} as the subject. Future versions " <<
250
+ "of Shoulda will require an explicit subject using the " <<
251
+ "subject class method. Add this after your setup to avoid " <<
252
+ "this warning: subject { #{ivar} }"
253
+ instance
254
+ else
255
+ klass.new
256
+ end
257
+ else
258
+ object_or_klass
259
+ end
260
+ end
261
+
262
+ def instance_variable_name_for(klass) # :nodoc:
263
+ klass.to_s.split('::').last.underscore
264
+ end
265
+ end
266
+
267
+ class Context # :nodoc:
268
+
269
+ attr_accessor :name # my name
270
+ attr_accessor :parent # may be another context, or the original test::unit class.
271
+ attr_accessor :subcontexts # array of contexts nested under myself
272
+ attr_accessor :setup_blocks # blocks given via setup methods
273
+ attr_accessor :teardown_blocks # blocks given via teardown methods
274
+ attr_accessor :shoulds # array of hashes representing the should statements
275
+ attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
276
+ attr_accessor :subject_block
277
+
278
+ def initialize(name, parent, &blk)
279
+ Shoulda.add_context(self)
280
+ self.name = name
281
+ self.parent = parent
282
+ self.setup_blocks = []
283
+ self.teardown_blocks = []
284
+ self.shoulds = []
285
+ self.should_eventuallys = []
286
+ self.subcontexts = []
287
+
288
+ merge_block(&blk)
289
+ Shoulda.remove_context
290
+ end
291
+
292
+ def merge_block(&blk)
293
+ blk.bind(self).call
294
+ end
295
+
296
+ def context(name, &blk)
297
+ self.subcontexts << Context.new(name, self, &blk)
298
+ end
299
+
300
+ def setup(&blk)
301
+ self.setup_blocks << blk
302
+ end
303
+
304
+ def teardown(&blk)
305
+ self.teardown_blocks << blk
306
+ end
307
+
308
+ def should(name, options = {}, &blk)
309
+ if block_given?
310
+ self.shoulds << { :name => name, :before => options[:before], :block => blk }
311
+ else
312
+ self.should_eventuallys << { :name => name }
313
+ end
314
+ end
315
+
316
+ def should_eventually(name, &blk)
317
+ self.should_eventuallys << { :name => name, :block => blk }
318
+ end
319
+
320
+ def subject(&block)
321
+ self.subject_block = block
322
+ end
323
+
324
+ def full_name
325
+ parent_name = parent.full_name if am_subcontext?
326
+ return [parent_name, name].join(" ").strip
327
+ end
328
+
329
+ def am_subcontext?
330
+ parent.is_a?(self.class) # my parent is the same class as myself.
331
+ end
332
+
333
+ def test_unit_class
334
+ am_subcontext? ? parent.test_unit_class : parent
335
+ end
336
+
337
+ def create_test_from_should_hash(should)
338
+ test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
339
+
340
+ if test_unit_class.instance_methods.include?(test_name.to_s)
341
+ warn " * WARNING: '#{test_name}' is already defined"
342
+ end
343
+
344
+ context = self
345
+ test_unit_class.send(:define_method, test_name) do
346
+ @shoulda_context = context
347
+ begin
348
+ context.run_parent_setup_blocks(self)
349
+ should[:before].bind(self).call if should[:before]
350
+ context.run_current_setup_blocks(self)
351
+ should[:block].bind(self).call
352
+ ensure
353
+ context.run_all_teardown_blocks(self)
354
+ end
355
+ end
356
+ end
357
+
358
+ def run_all_setup_blocks(binding)
359
+ run_parent_setup_blocks(binding)
360
+ run_current_setup_blocks(binding)
361
+ end
362
+
363
+ def run_parent_setup_blocks(binding)
364
+ self.parent.run_all_setup_blocks(binding) if am_subcontext?
365
+ end
366
+
367
+ def run_current_setup_blocks(binding)
368
+ setup_blocks.each do |setup_block|
369
+ setup_block.bind(binding).call
370
+ end
371
+ end
372
+
373
+ def run_all_teardown_blocks(binding)
374
+ teardown_blocks.reverse.each do |teardown_block|
375
+ teardown_block.bind(binding).call
376
+ end
377
+ self.parent.run_all_teardown_blocks(binding) if am_subcontext?
378
+ end
379
+
380
+ def print_should_eventuallys
381
+ should_eventuallys.each do |should|
382
+ test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
383
+ puts " * DEFERRED: " + test_name
384
+ end
385
+ end
386
+
387
+ def build
388
+ shoulds.each do |should|
389
+ create_test_from_should_hash(should)
390
+ end
391
+
392
+ subcontexts.each { |context| context.build }
393
+
394
+ print_should_eventuallys
395
+ end
396
+
397
+ def method_missing(method, *args, &blk)
398
+ test_unit_class.send(method, *args, &blk)
399
+ end
400
+
401
+ end
402
+ end