PageRenderTime 76ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rr/double_definitions/double_definition.rb

http://github.com/btakita/rr
Ruby | 367 lines | 282 code | 16 blank | 69 comment | 0 complexity | 15d61f06bff463e2f6cfa5fbe8c464ba MD5 | raw file
  1. module RR
  2. module DoubleDefinitions
  3. class DoubleDefinition #:nodoc:
  4. ORIGINAL_METHOD = Object.new
  5. attr_accessor(
  6. :argument_expectation,
  7. :times_matcher,
  8. :implementation,
  9. :after_call_proc,
  10. :yields_value,
  11. :double,
  12. :double_definition_create
  13. )
  14. include Space::Reader
  15. def initialize(double_definition_create)
  16. @implementation = nil
  17. @argument_expectation = nil
  18. @times_matcher = nil
  19. @after_call_proc = nil
  20. @yields_value = nil
  21. @double_definition_create = double_definition_create
  22. @ordered = false
  23. @verbose = false
  24. @verify_method_signature = false
  25. end
  26. def subject
  27. double_definition_create.subject
  28. end
  29. def root_subject
  30. double_definition_create.root_subject
  31. end
  32. module ArgumentDefinitionConstructionMethods
  33. # Double#with sets the expectation that the Double will receive
  34. # the passed in arguments.
  35. #
  36. # Passing in a block sets the return value.
  37. #
  38. # mock(subject).method_name.with(1, 2) {:return_value}
  39. def with(*args, &return_value_block)
  40. @argument_expectation = Expectations::ArgumentEqualityExpectation.new(*args)
  41. install_method_callback return_value_block
  42. self
  43. end
  44. # Double#with_any_args sets the expectation that the Double can receive
  45. # any arguments.
  46. #
  47. # Passing in a block sets the return value.
  48. #
  49. # mock(subject).method_name.with_any_args {:return_value}
  50. def with_any_args(&return_value_block)
  51. @argument_expectation = Expectations::AnyArgumentExpectation.new
  52. install_method_callback return_value_block
  53. self
  54. end
  55. # Double#with_no_args sets the expectation that the Double will receive
  56. # no arguments.
  57. #
  58. # Passing in a block sets the return value.
  59. #
  60. # mock(subject).method_name.with_no_args {:return_value}
  61. def with_no_args(&return_value_block)
  62. @argument_expectation = Expectations::ArgumentEqualityExpectation.new()
  63. install_method_callback return_value_block
  64. self
  65. end
  66. end
  67. include ArgumentDefinitionConstructionMethods
  68. module TimesDefinitionConstructionMethods
  69. # Double#never sets the expectation that the Double will never be
  70. # called.
  71. #
  72. # This method does not accept a block because it will never be called.
  73. #
  74. # mock(subject).method_name.never
  75. def never
  76. @times_matcher = TimesCalledMatchers::NeverMatcher.new
  77. self
  78. end
  79. # Double#once sets the expectation that the Double will be called
  80. # 1 time.
  81. #
  82. # Passing in a block sets the return value.
  83. #
  84. # mock(subject).method_name.once {:return_value}
  85. def once(&return_value_block)
  86. @times_matcher = TimesCalledMatchers::IntegerMatcher.new(1)
  87. install_method_callback return_value_block
  88. self
  89. end
  90. # Double#twice sets the expectation that the Double will be called
  91. # 2 times.
  92. #
  93. # Passing in a block sets the return value.
  94. #
  95. # mock(subject).method_name.twice {:return_value}
  96. def twice(&return_value_block)
  97. @times_matcher = TimesCalledMatchers::IntegerMatcher.new(2)
  98. install_method_callback return_value_block
  99. self
  100. end
  101. # Double#at_least sets the expectation that the Double
  102. # will be called at least n times.
  103. # It works by creating a TimesCalledExpectation.
  104. #
  105. # Passing in a block sets the return value.
  106. #
  107. # mock(subject).method_name.at_least(4) {:return_value}
  108. def at_least(number, &return_value_block)
  109. @times_matcher = TimesCalledMatchers::AtLeastMatcher.new(number)
  110. install_method_callback return_value_block
  111. self
  112. end
  113. # Double#at_most allows sets the expectation that the Double
  114. # will be called at most n times.
  115. # It works by creating a TimesCalledExpectation.
  116. #
  117. # Passing in a block sets the return value.
  118. #
  119. # mock(subject).method_name.at_most(4) {:return_value}
  120. def at_most(number, &return_value_block)
  121. @times_matcher = TimesCalledMatchers::AtMostMatcher.new(number)
  122. install_method_callback return_value_block
  123. self
  124. end
  125. # Double#any_number_of_times sets an that the Double will be called
  126. # any number of times. This effectively removes the times called expectation
  127. # from the Doublen
  128. #
  129. # Passing in a block sets the return value.
  130. #
  131. # mock(subject).method_name.any_number_of_times
  132. def any_number_of_times(&return_value_block)
  133. @times_matcher = TimesCalledMatchers::AnyTimesMatcher.new
  134. install_method_callback return_value_block
  135. self
  136. end
  137. alias_method :any_times, :any_number_of_times
  138. # Double#times creates an TimesCalledExpectation of the passed
  139. # in number.
  140. #
  141. # Passing in a block sets the return value.
  142. #
  143. # mock(subject).method_name.times(4) {:return_value}
  144. def times(matcher_value, &return_value_block)
  145. @times_matcher = TimesCalledMatchers::TimesCalledMatcher.create(matcher_value)
  146. install_method_callback return_value_block
  147. self
  148. end
  149. end
  150. include TimesDefinitionConstructionMethods
  151. module DefinitionConstructionMethods
  152. # Double#ordered sets the Double to have an ordered
  153. # expectation.
  154. #
  155. # Passing in a block sets the return value.
  156. #
  157. # mock(subject).method_name.ordered {return_value}
  158. def ordered(&return_value_block)
  159. raise(
  160. Errors::DoubleDefinitionError,
  161. "Double Definitions must have a dedicated Double to be ordered. " <<
  162. "For example, using instance_of does not allow ordered to be used. " <<
  163. "proxy the class's #new method instead."
  164. ) unless @double
  165. @ordered = true
  166. space.register_ordered_double(@double)
  167. install_method_callback return_value_block
  168. DoubleDefinitionCreateBlankSlate.new(double_definition_create)
  169. end
  170. alias_method :then, :ordered
  171. # Double#yields sets the Double to invoke a passed in block when
  172. # the Double is called.
  173. # An Expection will be raised if no block is passed in when the
  174. # Double is called.
  175. #
  176. # Passing in a block sets the return value.
  177. #
  178. # mock(subject).method_name.yields(yield_arg1, yield_arg2) {return_value}
  179. # subject.method_name {|yield_arg1, yield_arg2|}
  180. def yields(*args, &return_value_block)
  181. @yields_value = args
  182. install_method_callback return_value_block
  183. self
  184. end
  185. # Double#after_call creates a callback that occurs after call
  186. # is called. The passed in block receives the return value of
  187. # the Double being called.
  188. # An Expection will be raised if no block is passed in.
  189. #
  190. # mock(subject).method_name {return_value}.after_call {|return_value|}
  191. # subject.method_name # return_value
  192. #
  193. # This feature is built into proxies.
  194. # mock.proxy(User).find('1') {|user| mock(user).valid? {false}}
  195. def after_call(&after_call_proc)
  196. raise ArgumentError, "after_call expects a block" unless after_call_proc
  197. @after_call_proc = after_call_proc
  198. self
  199. end
  200. # Double#verbose sets the Double to print out each method call it receives.
  201. #
  202. # Passing in a block sets the return value
  203. def verbose(&after_call_proc)
  204. @verbose = true
  205. @after_call_proc = after_call_proc
  206. self
  207. end
  208. # Double#returns accepts an argument value or a block.
  209. # It will raise an ArgumentError if both are passed in.
  210. #
  211. # Passing in a block causes Double to return the return value of
  212. # the passed in block.
  213. #
  214. # Passing in an argument causes Double to return the argument.
  215. def returns(*args, &implementation)
  216. if !args.empty? && implementation
  217. raise ArgumentError, "returns cannot accept both an argument and a block"
  218. end
  219. if implementation
  220. install_method_callback implementation
  221. else
  222. install_method_callback(lambda do |*lambda_args|
  223. args.first
  224. end)
  225. end
  226. self
  227. end
  228. def implemented_by_original_method
  229. implemented_by ORIGINAL_METHOD
  230. self
  231. end
  232. # Double#implemented_by sets the implementation of the Double.
  233. # This method takes a Proc or a Method. Passing in a Method allows
  234. # the Double to accept blocks.
  235. #
  236. # obj = Object.new
  237. # def obj.foobar
  238. # yield(1)
  239. # end
  240. # mock(obj).method_name.implemented_by(obj.method(:foobar))
  241. def implemented_by(implementation)
  242. @implementation = implementation
  243. self
  244. end
  245. def verify_method_signature
  246. @verify_method_signature = true
  247. self
  248. end
  249. alias_method :strong, :verify_method_signature
  250. protected
  251. def install_method_callback(block)
  252. if block
  253. if implementation_is_original_method?
  254. after_call(&block)
  255. else
  256. implemented_by block
  257. end
  258. end
  259. end
  260. end
  261. include DefinitionConstructionMethods
  262. module StateQueryMethods
  263. # Double#ordered? returns true when the Double is ordered.
  264. #
  265. # mock(subject).method_name.ordered?
  266. def ordered?
  267. @ordered
  268. end
  269. # Double#verbose? returns true when verbose has been called on it. It returns
  270. # true when the double is set to print each method call it receives.
  271. def verbose?
  272. @verbose ? true : false
  273. end
  274. def exact_match?(*arguments)
  275. raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
  276. @argument_expectation.exact_match?(*arguments)
  277. end
  278. def wildcard_match?(*arguments)
  279. raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @argument_expectation
  280. @argument_expectation.wildcard_match?(*arguments)
  281. end
  282. def terminal?
  283. raise(Errors::DoubleDefinitionError, "#argument_expectation must be defined on #{inspect}") unless @times_matcher
  284. @times_matcher.terminal?
  285. end
  286. def expected_arguments
  287. argument_expectation ? argument_expectation.expected_arguments : []
  288. end
  289. def implementation_is_original_method?
  290. implementation_strategy.is_a?(Strategies::Implementation::Proxy)
  291. end
  292. def verify_method_signature?
  293. !!@verify_method_signature
  294. end
  295. alias_method :strong?, :verify_method_signature?
  296. protected
  297. def implementation_strategy
  298. double_definition_create.implementation_strategy
  299. end
  300. end
  301. include StateQueryMethods
  302. include ::RR::DoubleDefinitions::Strategies::StrategyMethods
  303. def mock(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  304. ChildDoubleDefinitionCreate.new(self).mock(subject, method_name, &definition_eval_block)
  305. end
  306. def stub(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  307. ChildDoubleDefinitionCreate.new(self).stub(subject, method_name, &definition_eval_block)
  308. end
  309. def dont_allow(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  310. ChildDoubleDefinitionCreate.new(self).dont_allow(subject, method_name, &definition_eval_block)
  311. end
  312. def proxy(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  313. ChildDoubleDefinitionCreate.new(self).proxy(subject, method_name, &definition_eval_block)
  314. end
  315. def strong(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  316. ChildDoubleDefinitionCreate.new(self).strong(subject, method_name, &definition_eval_block)
  317. end
  318. def any_instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  319. ChildDoubleDefinitionCreate.new(self).any_instance_of(subject, method_name, &definition_eval_block)
  320. end
  321. def instance_of(subject=DoubleDefinitionCreate::NO_SUBJECT, method_name=nil, &definition_eval_block)
  322. ChildDoubleDefinitionCreate.new(self).instance_of(subject, method_name, &definition_eval_block)
  323. end
  324. end
  325. end
  326. end