PageRenderTime 60ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/plugins/shoulda/lib/shoulda/context.rb

https://github.com/technicalpickles/flockup
Ruby | 306 lines | 153 code | 36 blank | 117 comment | 8 complexity | 434064496da4eaf77a1cb63443d95056 MD5 | raw file
Possible License(s): GPL-2.0
  1. module Thoughtbot # :nodoc:
  2. module Shoulda
  3. VERSION = '2.0.5'
  4. class << self
  5. attr_accessor :contexts
  6. def contexts # :nodoc:
  7. @contexts ||= []
  8. end
  9. def current_context # :nodoc:
  10. self.contexts.last
  11. end
  12. def add_context(context) # :nodoc:
  13. self.contexts.push(context)
  14. end
  15. def remove_context # :nodoc:
  16. self.contexts.pop
  17. end
  18. end
  19. # == Should statements
  20. #
  21. # Should statements are just syntactic sugar over normal Test::Unit test methods. A should block
  22. # contains all the normal code and assertions you're used to seeing, with the added benefit that
  23. # they can be wrapped inside context blocks (see below).
  24. #
  25. # === Example:
  26. #
  27. # class UserTest << Test::Unit::TestCase
  28. #
  29. # def setup
  30. # @user = User.new("John", "Doe")
  31. # end
  32. #
  33. # should "return its full name"
  34. # assert_equal 'John Doe', @user.full_name
  35. # end
  36. #
  37. # end
  38. #
  39. # ...will produce the following test:
  40. # * <tt>"test: User should return its full name. "</tt>
  41. #
  42. # Note: The part before <tt>should</tt> in the test name is gleamed from the name of the Test::Unit class.
  43. #
  44. # Should statements can also take a Proc as a <tt>:before </tt>option. This proc runs after any
  45. # parent context's setups but before the current context's setup.
  46. #
  47. # === Example:
  48. #
  49. # context "Some context" do
  50. # setup { puts("I run after the :before proc") }
  51. #
  52. # should "run a :before proc", :before => lambda { puts("I run before the setup") } do
  53. # assert true
  54. # end
  55. # end
  56. def should(name, options = {}, &blk)
  57. if Shoulda.current_context
  58. block_given? ? Shoulda.current_context.should(name, options, &blk) : Should.current_context.should_eventually(name)
  59. else
  60. context_name = self.name.gsub(/Test/, "")
  61. context = Thoughtbot::Shoulda::Context.new(context_name, self) do
  62. block_given? ? should(name, options, &blk) : should_eventually(name)
  63. end
  64. context.build
  65. end
  66. end
  67. # == Before statements
  68. #
  69. # Before statements are should statements that run before the current
  70. # context's setup. These are especially useful when setting expectations.
  71. #
  72. # === Example:
  73. #
  74. # class UserControllerTest < Test::Unit::TestCase
  75. # context "the index action" do
  76. # setup do
  77. # @users = [Factory(:user)]
  78. # User.stubs(:find).returns(@users)
  79. # end
  80. #
  81. # context "on GET" do
  82. # setup { get :index }
  83. #
  84. # should_respond_with :success
  85. #
  86. # # runs before "get :index"
  87. # before_should "find all users" do
  88. # User.expects(:find).with(:all).returns(@users)
  89. # end
  90. # end
  91. # end
  92. # end
  93. def before_should(name, &blk)
  94. should(name, :before => blk) { assert true }
  95. end
  96. # Just like should, but never runs, and instead prints an 'X' in the Test::Unit output.
  97. def should_eventually(name, options = {}, &blk)
  98. context_name = self.name.gsub(/Test/, "")
  99. context = Thoughtbot::Shoulda::Context.new(context_name, self) do
  100. should_eventually(name, &blk)
  101. end
  102. context.build
  103. end
  104. # == Contexts
  105. #
  106. # A context block groups should statements under a common set of setup/teardown methods.
  107. # Context blocks can be arbitrarily nested, and can do wonders for improving the maintainability
  108. # and readability of your test code.
  109. #
  110. # A context block can contain setup, should, should_eventually, and teardown blocks.
  111. #
  112. # class UserTest << Test::Unit::TestCase
  113. # context "A User instance" do
  114. # setup do
  115. # @user = User.find(:first)
  116. # end
  117. #
  118. # should "return its full name"
  119. # assert_equal 'John Doe', @user.full_name
  120. # end
  121. # end
  122. # end
  123. #
  124. # This code will produce the method <tt>"test: A User instance should return its full name. "</tt>.
  125. #
  126. # Contexts may be nested. Nested contexts run their setup blocks from out to in before each
  127. # should statement. They then run their teardown blocks from in to out after each should statement.
  128. #
  129. # class UserTest << Test::Unit::TestCase
  130. # context "A User instance" do
  131. # setup do
  132. # @user = User.find(:first)
  133. # end
  134. #
  135. # should "return its full name"
  136. # assert_equal 'John Doe', @user.full_name
  137. # end
  138. #
  139. # context "with a profile" do
  140. # setup do
  141. # @user.profile = Profile.find(:first)
  142. # end
  143. #
  144. # should "return true when sent :has_profile?"
  145. # assert @user.has_profile?
  146. # end
  147. # end
  148. # end
  149. # end
  150. #
  151. # This code will produce the following methods
  152. # * <tt>"test: A User instance should return its full name. "</tt>
  153. # * <tt>"test: A User instance with a profile should return true when sent :has_profile?. "</tt>
  154. #
  155. # <b>Just like should statements, a context block can exist next to normal <tt>def test_the_old_way; end</tt>
  156. # tests</b>. This means you do not have to fully commit to the context/should syntax in a test file.
  157. def context(name, &blk)
  158. if Shoulda.current_context
  159. Shoulda.current_context.context(name, &blk)
  160. else
  161. context = Thoughtbot::Shoulda::Context.new(name, self, &blk)
  162. context.build
  163. end
  164. end
  165. class Context # :nodoc:
  166. attr_accessor :name # my name
  167. attr_accessor :parent # may be another context, or the original test::unit class.
  168. attr_accessor :subcontexts # array of contexts nested under myself
  169. attr_accessor :setup_blocks # blocks given via setup methods
  170. attr_accessor :teardown_blocks # blocks given via teardown methods
  171. attr_accessor :shoulds # array of hashes representing the should statements
  172. attr_accessor :should_eventuallys # array of hashes representing the should eventually statements
  173. def initialize(name, parent, &blk)
  174. Shoulda.add_context(self)
  175. self.name = name
  176. self.parent = parent
  177. self.setup_blocks = []
  178. self.teardown_blocks = []
  179. self.shoulds = []
  180. self.should_eventuallys = []
  181. self.subcontexts = []
  182. merge_block(&blk)
  183. Shoulda.remove_context
  184. end
  185. def merge_block(&blk)
  186. blk.bind(self).call
  187. end
  188. def context(name, &blk)
  189. self.subcontexts << Context.new(name, self, &blk)
  190. end
  191. def setup(&blk)
  192. self.setup_blocks << blk
  193. end
  194. def teardown(&blk)
  195. self.teardown_blocks << blk
  196. end
  197. def should(name, options = {}, &blk)
  198. if block_given?
  199. self.shoulds << { :name => name, :before => options[:before], :block => blk }
  200. else
  201. self.should_eventuallys << { :name => name }
  202. end
  203. end
  204. def should_eventually(name, &blk)
  205. self.should_eventuallys << { :name => name, :block => blk }
  206. end
  207. def full_name
  208. parent_name = parent.full_name if am_subcontext?
  209. return [parent_name, name].join(" ").strip
  210. end
  211. def am_subcontext?
  212. parent.is_a?(self.class) # my parent is the same class as myself.
  213. end
  214. def test_unit_class
  215. am_subcontext? ? parent.test_unit_class : parent
  216. end
  217. def create_test_from_should_hash(should)
  218. test_name = ["test:", full_name, "should", "#{should[:name]}. "].flatten.join(' ').to_sym
  219. if test_unit_class.instance_methods.include?(test_name.to_s)
  220. warn " * WARNING: '#{test_name}' is already defined"
  221. end
  222. context = self
  223. test_unit_class.send(:define_method, test_name) do
  224. begin
  225. context.run_parent_setup_blocks(self)
  226. should[:before].bind(self).call if should[:before]
  227. context.run_current_setup_blocks(self)
  228. should[:block].bind(self).call
  229. ensure
  230. context.run_all_teardown_blocks(self)
  231. end
  232. end
  233. end
  234. def run_all_setup_blocks(binding)
  235. run_parent_setup_blocks(binding)
  236. run_current_setup_blocks(binding)
  237. end
  238. def run_parent_setup_blocks(binding)
  239. self.parent.run_all_setup_blocks(binding) if am_subcontext?
  240. end
  241. def run_current_setup_blocks(binding)
  242. setup_blocks.each do |setup_block|
  243. setup_block.bind(binding).call
  244. end
  245. end
  246. def run_all_teardown_blocks(binding)
  247. teardown_blocks.reverse.each do |teardown_block|
  248. teardown_block.bind(binding).call
  249. end
  250. self.parent.run_all_teardown_blocks(binding) if am_subcontext?
  251. end
  252. def print_should_eventuallys
  253. should_eventuallys.each do |should|
  254. test_name = [full_name, "should", "#{should[:name]}. "].flatten.join(' ')
  255. puts " * DEFERRED: " + test_name
  256. end
  257. end
  258. def build
  259. shoulds.each do |should|
  260. create_test_from_should_hash(should)
  261. end
  262. subcontexts.each { |context| context.build }
  263. print_should_eventuallys
  264. end
  265. def method_missing(method, *args, &blk)
  266. test_unit_class.send(method, *args, &blk)
  267. end
  268. end
  269. end
  270. end