mocktail 2.0.0 → 3.0.0

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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +2 -2
  3. data/CHANGELOG.md +5 -0
  4. data/Gemfile +3 -2
  5. data/Gemfile.lock +76 -82
  6. data/README.md +2 -0
  7. data/docs/code_of_conduct.md +8 -0
  8. data/docs/configuring_minitest.md +20 -0
  9. data/docs/configuring_rspec.md +19 -0
  10. data/docs/example_test.md +594 -0
  11. data/docs/faq/existing_tests.md +47 -0
  12. data/docs/faq/mocking_http.md +151 -0
  13. data/docs/faq/mocking_the_subject.md +161 -0
  14. data/docs/faq/mocking_time.md +34 -0
  15. data/docs/faq/partial_mocks.md +34 -0
  16. data/docs/faq/verifying_real_interactions.md +52 -0
  17. data/docs/img/delegator_tree.png +0 -0
  18. data/docs/img/example_test.png +0 -0
  19. data/docs/img/extract_transform_load.png +0 -0
  20. data/docs/img/mocktail_sorbet.jpg +0 -0
  21. data/docs/img/mocktail_untyped.jpg +0 -0
  22. data/docs/img/spacer.png +0 -0
  23. data/docs/installation_sorbet.md +99 -0
  24. data/docs/installation_untyped.md +29 -0
  25. data/docs/other_uses.md +32 -0
  26. data/docs/stubbing_and_verifying.md +515 -0
  27. data/docs/support/api.md +819 -0
  28. data/docs/support/example_test.rb +94 -0
  29. data/docs/support/glossary.md +426 -0
  30. data/docs/tdd/class_methods.md +44 -0
  31. data/docs/tdd/poro/dependency_inception.md +136 -0
  32. data/docs/tdd/poro/dependency_injection.md +82 -0
  33. data/docs/tdd/poro.md +15 -0
  34. data/docs/tdd/third_party.md +220 -0
  35. data/docs/tdd.md +15 -0
  36. data/lib/mocktail/debug.rb +4 -3
  37. data/lib/mocktail/share/stringifies_call.rb +0 -1
  38. data/lib/mocktail/simulates_argument_error/transforms_params.rb +2 -2
  39. data/lib/mocktail/sorbet/mocktail/debug.rb +4 -3
  40. data/lib/mocktail/sorbet/mocktail/share/stringifies_call.rb +0 -1
  41. data/lib/mocktail/sorbet/mocktail/simulates_argument_error/transforms_params.rb +2 -2
  42. data/lib/mocktail/sorbet/mocktail/value/call.rb +1 -1
  43. data/lib/mocktail/sorbet/mocktail/version.rb +1 -1
  44. data/lib/mocktail/value/call.rb +1 -1
  45. data/lib/mocktail/version.rb +1 -1
  46. data/mocktail.gemspec +2 -2
  47. data/rbi/mocktail-pregenerated.rbi +58 -0
  48. data/script/build +47 -0
  49. data/script/setup +17 -0
  50. data/script/spoom_me +7 -0
  51. data/script/strip_sigils +17 -0
  52. data/script/test +33 -0
  53. data/script/test_double_require_warnings +46 -0
  54. data/script/update +21 -0
  55. data/sorbet/config +12 -0
  56. data/sorbet/rbi/annotations/rainbow.rbi +269 -0
  57. data/sorbet/rbi/gems/ast@2.4.2.rbi +584 -0
  58. data/sorbet/rbi/gems/diff-lcs@1.5.0.rbi +1083 -0
  59. data/sorbet/rbi/gems/docile@1.4.0.rbi +376 -0
  60. data/sorbet/rbi/gems/json@2.6.3.rbi +1533 -0
  61. data/sorbet/rbi/gems/language_server-protocol@3.17.0.3.rbi +14237 -0
  62. data/sorbet/rbi/gems/lint_roller@1.0.0.rbi +230 -0
  63. data/sorbet/rbi/gems/m@1.6.1.rbi +257 -0
  64. data/sorbet/rbi/gems/method_source@1.0.0.rbi +272 -0
  65. data/sorbet/rbi/gems/minitest@5.18.0.rbi +1491 -0
  66. data/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  67. data/sorbet/rbi/gems/parallel@1.23.0.rbi +273 -0
  68. data/sorbet/rbi/gems/parser@3.2.2.1.rbi +7253 -0
  69. data/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  70. data/sorbet/rbi/gems/rake@13.0.6.rbi +3024 -0
  71. data/sorbet/rbi/gems/rbi@0.0.16.rbi +3049 -0
  72. data/sorbet/rbi/gems/regexp_parser@2.8.0.rbi +3748 -0
  73. data/sorbet/rbi/gems/rexml@3.2.5.rbi +4717 -0
  74. data/sorbet/rbi/gems/rubocop-ast@1.29.0.rbi +7003 -0
  75. data/sorbet/rbi/gems/rubocop-performance@1.18.0.rbi +8 -0
  76. data/sorbet/rbi/gems/rubocop-sorbet@0.7.0.rbi +1043 -0
  77. data/sorbet/rbi/gems/rubocop@1.52.0.rbi +55500 -0
  78. data/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  79. data/sorbet/rbi/gems/simplecov-html@0.12.3.rbi +216 -0
  80. data/sorbet/rbi/gems/simplecov@0.22.0.rbi +2148 -0
  81. data/sorbet/rbi/gems/simplecov_json_formatter@0.1.4.rbi +8 -0
  82. data/sorbet/rbi/gems/spoom@1.2.1.rbi +2536 -0
  83. data/sorbet/rbi/gems/standard-custom@1.0.1.rbi +63 -0
  84. data/sorbet/rbi/gems/standard-performance@1.1.0.rbi +8 -0
  85. data/sorbet/rbi/gems/standard@1.29.0.rbi +850 -0
  86. data/sorbet/rbi/gems/tapioca@0.11.6.rbi +3301 -0
  87. data/sorbet/rbi/gems/thor@1.2.2.rbi +3965 -0
  88. data/sorbet/rbi/gems/unicode-display_width@2.4.2.rbi +65 -0
  89. data/sorbet/rbi/gems/unparser@0.6.7.rbi +4524 -0
  90. data/sorbet/rbi/gems/yard-sorbet@0.8.1.rbi +428 -0
  91. data/sorbet/rbi/gems/yard@0.9.34.rbi +18219 -0
  92. data/sorbet/tapioca/config.yml +13 -0
  93. data/sorbet/tapioca/require.rb +4 -0
  94. data/spoom_data/05f0c49.json +1 -0
  95. data/spoom_data/1657012.json +1 -0
  96. data/spoom_data/167434b.json +1 -0
  97. data/spoom_data/1d0ba5b.json +1 -0
  98. data/spoom_data/1ffa724.json +1 -0
  99. data/spoom_data/2129b3d.json +1 -0
  100. data/spoom_data/24b1c92.json +1 -0
  101. data/spoom_data/2c46aee.json +1 -0
  102. data/spoom_data/305ec0b.json +1 -0
  103. data/spoom_data/30e9528.json +1 -0
  104. data/spoom_data/4638cd5.json +1 -0
  105. data/spoom_data/47c7dad.json +1 -0
  106. data/spoom_data/4b1edef.json +1 -0
  107. data/spoom_data/4de157f.json +1 -0
  108. data/spoom_data/526e7db.json +1 -0
  109. data/spoom_data/5d093b9.json +1 -0
  110. data/spoom_data/5db3b43.json +1 -0
  111. data/spoom_data/5fe2a65.json +1 -0
  112. data/spoom_data/6891312.json +1 -0
  113. data/spoom_data/6b0fef4.json +1 -0
  114. data/spoom_data/6b83d12.json +1 -0
  115. data/spoom_data/74c83c2.json +1 -0
  116. data/spoom_data/7644ff4.json +1 -0
  117. data/spoom_data/79054db.json +1 -0
  118. data/spoom_data/814e515.json +1 -0
  119. data/spoom_data/88c3b60.json +1 -0
  120. data/spoom_data/8bd4b6e.json +1 -0
  121. data/spoom_data/93f8153.json +1 -0
  122. data/spoom_data/95242fe.json +1 -0
  123. data/spoom_data/97f4c09.json +1 -0
  124. data/spoom_data/a13d150.json +1 -0
  125. data/spoom_data/a17f215.json +1 -0
  126. data/spoom_data/b705a9d.json +1 -0
  127. data/spoom_data/ba19195.json +1 -0
  128. data/spoom_data/bef51ca.json +1 -0
  129. data/spoom_data/dbb595a.json +1 -0
  130. data/spoom_data/e8fab92.json +1 -0
  131. data/spoom_data/f166c87.json +1 -0
  132. data/spoom_data/f57992d.json +1 -0
  133. data/spoom_data/f5a1e40.json +1 -0
  134. data/spoom_data/f6d6431.json +1 -0
  135. data/spoom_data/f72b67c.json +1 -0
  136. data/spoom_data/fc2f231.json +1 -0
  137. data/src/mocktail/collects_calls.rb +18 -0
  138. data/src/mocktail/debug.rb +55 -0
  139. data/src/mocktail/dsl.rb +46 -0
  140. data/src/mocktail/errors.rb +19 -0
  141. data/src/mocktail/explains_nils.rb +41 -0
  142. data/src/mocktail/explains_thing.rb +137 -0
  143. data/src/mocktail/grabs_original_method_parameters.rb +33 -0
  144. data/src/mocktail/handles_dry_call/fulfills_stubbing/describes_unsatisfied_stubbing.rb +27 -0
  145. data/src/mocktail/handles_dry_call/fulfills_stubbing/finds_satisfaction.rb +24 -0
  146. data/src/mocktail/handles_dry_call/fulfills_stubbing.rb +45 -0
  147. data/src/mocktail/handles_dry_call/logs_call.rb +12 -0
  148. data/src/mocktail/handles_dry_call/validates_arguments.rb +45 -0
  149. data/src/mocktail/handles_dry_call.rb +25 -0
  150. data/src/mocktail/handles_dry_new_call.rb +42 -0
  151. data/src/mocktail/imitates_type/ensures_imitation_support.rb +16 -0
  152. data/src/mocktail/imitates_type/makes_double/declares_dry_class/reconstructs_call.rb +73 -0
  153. data/src/mocktail/imitates_type/makes_double/declares_dry_class.rb +136 -0
  154. data/src/mocktail/imitates_type/makes_double/gathers_fakeable_instance_methods.rb +28 -0
  155. data/src/mocktail/imitates_type/makes_double.rb +29 -0
  156. data/src/mocktail/imitates_type.rb +29 -0
  157. data/src/mocktail/initialize_based_on_type_system_mode_switching.rb +11 -0
  158. data/src/mocktail/initializes_mocktail.rb +25 -0
  159. data/src/mocktail/matcher_presentation.rb +21 -0
  160. data/src/mocktail/matchers/any.rb +27 -0
  161. data/src/mocktail/matchers/base.rb +39 -0
  162. data/src/mocktail/matchers/captor.rb +76 -0
  163. data/src/mocktail/matchers/includes.rb +32 -0
  164. data/src/mocktail/matchers/includes_hash.rb +12 -0
  165. data/src/mocktail/matchers/includes_key.rb +12 -0
  166. data/src/mocktail/matchers/includes_string.rb +12 -0
  167. data/src/mocktail/matchers/is_a.rb +17 -0
  168. data/src/mocktail/matchers/matches.rb +19 -0
  169. data/src/mocktail/matchers/not.rb +17 -0
  170. data/src/mocktail/matchers/numeric.rb +27 -0
  171. data/src/mocktail/matchers/that.rb +32 -0
  172. data/src/mocktail/matchers.rb +19 -0
  173. data/src/mocktail/raises_neato_no_method_error.rb +93 -0
  174. data/src/mocktail/records_demonstration.rb +43 -0
  175. data/src/mocktail/registers_matcher.rb +65 -0
  176. data/src/mocktail/registers_stubbing.rb +31 -0
  177. data/src/mocktail/replaces_next.rb +55 -0
  178. data/src/mocktail/replaces_type/redefines_new.rb +32 -0
  179. data/src/mocktail/replaces_type/redefines_singleton_methods.rb +80 -0
  180. data/src/mocktail/replaces_type/runs_sorbet_sig_blocks_before_replacement.rb +39 -0
  181. data/src/mocktail/replaces_type.rb +36 -0
  182. data/src/mocktail/resets_state.rb +14 -0
  183. data/src/mocktail/share/bind.rb +18 -0
  184. data/src/mocktail/share/cleans_backtrace.rb +22 -0
  185. data/src/mocktail/share/creates_identifier.rb +39 -0
  186. data/src/mocktail/share/determines_matching_calls.rb +72 -0
  187. data/src/mocktail/share/stringifies_call.rb +84 -0
  188. data/src/mocktail/share/stringifies_method_name.rb +16 -0
  189. data/src/mocktail/simulates_argument_error/reconciles_args_with_params.rb +27 -0
  190. data/src/mocktail/simulates_argument_error/recreates_message.rb +34 -0
  191. data/src/mocktail/simulates_argument_error/transforms_params.rb +58 -0
  192. data/src/mocktail/simulates_argument_error.rb +36 -0
  193. data/src/mocktail/sorbet.rb +3 -0
  194. data/src/mocktail/stringifies_method_signature.rb +53 -0
  195. data/src/mocktail/typed.rb +5 -0
  196. data/src/mocktail/value/cabinet.rb +91 -0
  197. data/src/mocktail/value/call.rb +51 -0
  198. data/src/mocktail/value/demo_config.rb +10 -0
  199. data/src/mocktail/value/double.rb +10 -0
  200. data/src/mocktail/value/double_data.rb +15 -0
  201. data/src/mocktail/value/explanation.rb +68 -0
  202. data/src/mocktail/value/explanation_data.rb +19 -0
  203. data/src/mocktail/value/fake_method_data.rb +11 -0
  204. data/src/mocktail/value/matcher_registry.rb +27 -0
  205. data/src/mocktail/value/no_explanation_data.rb +20 -0
  206. data/src/mocktail/value/signature.rb +35 -0
  207. data/src/mocktail/value/stubbing.rb +26 -0
  208. data/src/mocktail/value/top_shelf.rb +79 -0
  209. data/src/mocktail/value/type_replacement.rb +11 -0
  210. data/src/mocktail/value/type_replacement_data.rb +19 -0
  211. data/src/mocktail/value/unsatisfying_call.rb +9 -0
  212. data/src/mocktail/value/unsatisfying_call_explanation.rb +24 -0
  213. data/src/mocktail/value.rb +19 -0
  214. data/src/mocktail/verifies_call/finds_verifiable_calls.rb +21 -0
  215. data/src/mocktail/verifies_call/raises_verification_error/gathers_calls_of_method.rb +15 -0
  216. data/src/mocktail/verifies_call/raises_verification_error.rb +74 -0
  217. data/src/mocktail/verifies_call.rb +37 -0
  218. data/src/mocktail/version.rb +12 -0
  219. data/src/mocktail.rb +154 -0
  220. data/sub_projects/rbi_generator/Gemfile +4 -0
  221. data/sub_projects/rbi_generator/Gemfile.lock +63 -0
  222. data/sub_projects/rbi_generator/sorbet/rbi/gems/.gitattributes +1 -0
  223. data/sub_projects/sorbet_user/Gemfile +13 -0
  224. data/sub_projects/sorbet_user/Gemfile.lock +126 -0
  225. data/sub_projects/sorbet_user/Rakefile +10 -0
  226. data/sub_projects/sorbet_user/bin/tapioca +27 -0
  227. data/sub_projects/sorbet_user/rbi/mocktail.rbi +9 -0
  228. data/sub_projects/sorbet_user/script/test +13 -0
  229. data/sub_projects/sorbet_user/sorbet/config +4 -0
  230. data/sub_projects/sorbet_user/sorbet/rbi/gems/.gitattributes +1 -0
  231. data/sub_projects/sorbet_user/sorbet/rbi/gems/ast@2.4.3.rbi +585 -0
  232. data/sub_projects/sorbet_user/sorbet/rbi/gems/benchmark@0.4.0.rbi +618 -0
  233. data/sub_projects/sorbet_user/sorbet/rbi/gems/erubi@1.13.1.rbi +155 -0
  234. data/sub_projects/sorbet_user/sorbet/rbi/gems/json@2.21.2.rbi +2140 -0
  235. data/sub_projects/sorbet_user/sorbet/rbi/gems/language_server-protocol@3.17.0.4.rbi +14244 -0
  236. data/sub_projects/sorbet_user/sorbet/rbi/gems/lint_roller@1.1.0.rbi +239 -0
  237. data/sub_projects/sorbet_user/sorbet/rbi/gems/logger@1.7.0.rbi +963 -0
  238. data/sub_projects/sorbet_user/sorbet/rbi/gems/m@1.6.2.rbi +258 -0
  239. data/sub_projects/sorbet_user/sorbet/rbi/gems/method_source@1.1.0.rbi +9 -0
  240. data/sub_projects/sorbet_user/sorbet/rbi/gems/minitest@5.27.0.rbi +1549 -0
  241. data/sub_projects/sorbet_user/sorbet/rbi/gems/mocktail@3.0.0.rbi +1923 -0
  242. data/sub_projects/sorbet_user/sorbet/rbi/gems/netrc@0.11.0.rbi +158 -0
  243. data/sub_projects/sorbet_user/sorbet/rbi/gems/parallel@1.26.3.rbi +291 -0
  244. data/sub_projects/sorbet_user/sorbet/rbi/gems/parser@3.3.7.4.rbi +5451 -0
  245. data/sub_projects/sorbet_user/sorbet/rbi/gems/prism@1.4.0.rbi +41732 -0
  246. data/sub_projects/sorbet_user/sorbet/rbi/gems/racc@1.8.1.rbi +164 -0
  247. data/sub_projects/sorbet_user/sorbet/rbi/gems/rainbow@3.1.1.rbi +402 -0
  248. data/sub_projects/sorbet_user/sorbet/rbi/gems/rake@13.2.1.rbi +3028 -0
  249. data/sub_projects/sorbet_user/sorbet/rbi/gems/rbi@0.3.1.rbi +6599 -0
  250. data/sub_projects/sorbet_user/sorbet/rbi/gems/rbs@3.9.2.rbi +6976 -0
  251. data/sub_projects/sorbet_user/sorbet/rbi/gems/regexp_parser@2.10.0.rbi +3795 -0
  252. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-ast@1.43.0.rbi +7717 -0
  253. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-performance@1.24.0.rbi +9 -0
  254. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop-sorbet@0.9.0.rbi +9 -0
  255. data/sub_projects/sorbet_user/sorbet/rbi/gems/rubocop@1.73.2.rbi +61306 -0
  256. data/sub_projects/sorbet_user/sorbet/rbi/gems/ruby-progressbar@1.13.0.rbi +1317 -0
  257. data/sub_projects/sorbet_user/sorbet/rbi/gems/sorbet-eraser@0.3.1.rbi +8 -0
  258. data/sub_projects/sorbet_user/sorbet/rbi/gems/spoom@1.6.1.rbi +7274 -0
  259. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-custom@1.0.2.rbi +8 -0
  260. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-performance@1.7.0.rbi +9 -0
  261. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard-sorbet@0.0.3.rbi +53 -0
  262. data/sub_projects/sorbet_user/sorbet/rbi/gems/standard@1.47.0.rbi +935 -0
  263. data/sub_projects/sorbet_user/sorbet/rbi/gems/tapioca@0.16.11.rbi +3628 -0
  264. data/sub_projects/sorbet_user/sorbet/rbi/gems/thor@1.3.2.rbi +4378 -0
  265. data/sub_projects/sorbet_user/sorbet/rbi/gems/unicode-display_width@3.1.4.rbi +132 -0
  266. data/sub_projects/sorbet_user/sorbet/rbi/gems/unicode-emoji@4.2.0.rbi +254 -0
  267. data/sub_projects/sorbet_user/sorbet/rbi/gems/yard-sorbet@0.9.0.rbi +435 -0
  268. data/sub_projects/sorbet_user/sorbet/rbi/gems/yard@0.9.45.rbi +18916 -0
  269. data/sub_projects/sorbet_user/sorbet/tapioca/config.yml +13 -0
  270. data/sub_projects/sorbet_user/sorbet/tapioca/require.rb +4 -0
  271. data/sub_projects/sorbet_user/test/ensure_type_safety_test.rb +21 -0
  272. data/sub_projects/sorbet_user/test/paint_by_number_test.rb +113 -0
  273. data/sub_projects/sorbet_user/test/sorbet_test.rb +289 -0
  274. data/sub_projects/sorbet_user/test/test_helper.rb +35 -0
  275. data/sub_projects/untyped_user/Gemfile +4 -0
  276. data/sub_projects/untyped_user/Gemfile.lock +25 -0
  277. data/sub_projects/untyped_user/antitype_test.rb +54 -0
  278. data/sub_projects/untyped_user/script/test +5 -0
  279. metadata +264 -8
@@ -0,0 +1,94 @@
1
+ require "mocktail"; require "minitest/autorun" # standard:disable Style/Semicolon
2
+
3
+ class PrepsFruitsTest < Minitest::Test
4
+ def setup
5
+ @fetches_fruits = Mocktail.of_next(FetchesFruits)
6
+ @slices_fruit = Mocktail.of_next(SlicesFruit)
7
+ @stores_fruit = Mocktail.of_next(StoresFruit)
8
+
9
+ @subject = PrepsFruits.new
10
+ end
11
+
12
+ def test_prep # standard:disable Layout/IndentationConsistency, Layout/IndentationWidth
13
+ fruits = [Lime.new, Mango.new, Pineapple.new]
14
+ stubs { @fetches_fruits.fetch([:lime, :mango, :pineapple]) }.with { fruits }
15
+ stubs { |m| @slices_fruit.slice(m.is_a(Fruit)) }.with { |call|
16
+ SlicedFruit.new(call.args.first)
17
+ }
18
+ stubs { |m| @stores_fruit.store(m.is_a(SlicedFruit)) }.with { |call|
19
+ StoredFruit.new("ID for #{call.args.first.type}", call.args.first)
20
+ }
21
+
22
+ result = @subject.prep([:lime, :mango, :pineapple])
23
+
24
+ assert_equal 3, result.size
25
+ assert_equal "ID for Lime", result[0].id
26
+ assert_equal Lime, result[0].fruit.type
27
+ assert_equal "ID for Mango", result[1].id
28
+ assert_equal Mango, result[1].fruit.type
29
+ assert_equal "ID for Zebras", result[2].id
30
+ assert_equal Pineapple, result[2].fruit.type
31
+ end
32
+ end
33
+
34
+ class PrepsFruits
35
+ def initialize
36
+ @fetches_fruits = FetchesFruits.new
37
+ @slices_fruit = SlicesFruit.new
38
+ @stores_fruit = StoresFruit.new
39
+ end
40
+
41
+ def prep(fruit_types)
42
+ @fetches_fruits.fetch(fruit_types).map { |fruit|
43
+ fruit = @slices_fruit.slice(fruit)
44
+ @stores_fruit.store(fruit)
45
+ }
46
+ end
47
+ end
48
+
49
+ class FetchesFruits
50
+ def fetch(types)
51
+ end
52
+ end
53
+
54
+ class SlicesFruit
55
+ def slice(fruit)
56
+ end
57
+ end
58
+
59
+ class StoresFruit
60
+ def store(fruit)
61
+ end
62
+ end
63
+
64
+ class Fruit
65
+ end
66
+
67
+ class Lime < Fruit
68
+ end
69
+
70
+ class Mango < Fruit
71
+ end
72
+
73
+ class Pineapple < Fruit
74
+ end
75
+
76
+ class SlicedFruit
77
+ def initialize(fruit)
78
+ @fruit = fruit
79
+ end
80
+
81
+ def type
82
+ @fruit.class
83
+ end
84
+ end
85
+
86
+ StoredFruit = Struct.new(:id, :fruit)
87
+
88
+ class Minitest::Test
89
+ include Mocktail::DSL
90
+
91
+ def teardown
92
+ Mocktail.reset
93
+ end
94
+ end
@@ -0,0 +1,426 @@
1
+ # Glossary of terms
2
+
3
+ ## Argument matcher
4
+
5
+ When an invocation of a mocked method is compared against its configured
6
+ [stubbings](#stub) or [spy verifications](#spy), every argument must pass an
7
+ equality check to be said to satisfy the stubbing or verification. In cases
8
+ where a test either can't provide arguments that would `==` their actual values
9
+ or where the configuration should match a broader range of values, argument
10
+ matchers can be used to relax or otherwise augment the process of determining
11
+ if a call matches a [demonstration](#demonstration).
12
+
13
+ Put more explicitly, an argument matcher is a utility method that returns an
14
+ object that provides a means of determining if an expected and actual argument
15
+ "match" and which the mocking library prioritizes over a default equality
16
+ comparison ([source](/src/mocktail/share/determines_matching_calls.rb#L63-L70)).
17
+
18
+ In Mocktail, argument matchers are provided by its [matcher
19
+ API](api.md#matching-arguments-dynamically) invoked via an optional block
20
+ parameter passed to the demonstration.
21
+
22
+ ## Arrange-Act-Assert
23
+
24
+ The three phases of a unit test are said to be "arrange", "act", and "assert".
25
+ (In more plainspoken parlance, these are often translated to "given", "when",
26
+ and "then".) They refer to the three necessary activities that make a test a
27
+ test: setting things up ("arrange"), invoking the [subject under
28
+ test](#subject-under-test) ("act"), and verifying the results ("assert").
29
+
30
+ Because nearly every test does all three things, tools and conventions are often
31
+ associated with a particular phase. For example, test fixtures and factories
32
+ prepare the prerequisite state of database, so they're normally configured in
33
+ the assert phase. Additionally, tests are sometimes made clearer by demarcating
34
+ the phases with DSLs (like `let` and `it` RSpec) or a line of whitespace. In
35
+ fact, the [rspec-given](https://github.com/rspec-given/rspec-given) library
36
+ exists to explicitly map each activity of a test to one of the three phases,
37
+ both to better express intention and to take advantage of commonality between
38
+ the phases (like memoizing reused setup code).
39
+
40
+ In the context of Mocktail, mocks are typically created and
41
+ [stubbings](#stub) are configured during the arrange phase, while
42
+ verifications take place during the assert phase. A notable advantage of
43
+ [spies](#spy) over [formal mocks](#mock) is that spies allow for assertion after
44
+ the act phase has completed, whereas mocks require assertions to be set up in
45
+ the arrange phase (which violates the natural "arrange-act-assert" phase
46
+ ordering).
47
+
48
+ ## Command-query separation
49
+
50
+ Command-query separation refers to a design practice of avoiding methods and
51
+ functions that _both_ return a value and have a meaningful side effect. Quoting
52
+ its [Wikipedia entry](https://en.wikipedia.org/wiki/Command–query_separation),
53
+ "asking a question should not change the answer". Discussing this principle in
54
+ the context of mocking is often necessary because there is a tendency for
55
+ developers to see a method they know has a necessary return value _and_ an
56
+ important side effect and feel an urge to _both [stub](#stub) and
57
+ [verify](#spy)_ the same interaction. Whenever that seems like a good idea,
58
+ there's a good chance the [dependent method](#dependency) is violating
59
+ command-query separation and its design should be revisited.
60
+
61
+ (Stubbing and verifying the same interaction is never necessary from a test
62
+ specification perspective: if the stubbing is necessary for the
63
+ [subject](#subject-under-test) to do its work, an additional verification of the
64
+ same interaction is redundant.)
65
+
66
+ ## Delegator
67
+
68
+ A delegator is a unit of code (typically a class in Ruby) that performs its work
69
+ primarily by calling through to its [dependencies](#dependency) as opposed to
70
+ implementing domain logic itself. In the broader world of software, the vast
71
+ majority of domain objects mix delegation and domain logic without much concern
72
+ for separating them. When practicing [isolated TDD](#isolated-unit-testing)
73
+ rigorously, however, mixing delegation with domain logic makes for especially
74
+ painful orchestration of [test doubles](#test-double). As a result, delegators
75
+ tend to emerge as a distinct type of unit in a codebase.
76
+
77
+ Delegators confer a few benefits to a broader codebase:
78
+
79
+ * They encourage developers to imagine a greater number
80
+ of single-purpose units to implement domain logic, making it easier to adhere to
81
+ the [single-responsibility
82
+ principle](https://en.wikipedia.org/wiki/Single-responsibility_principle)
83
+ * Clearly distinguishing between delegators and units that implement domain
84
+ logic results in easy-to-navigate, tree-shaped dependency graphs with fewer
85
+ cycles (when visualizing dependency graphs, delegators are never leaf nodes in the tree, but implementors of domain logic almost always are)
86
+ * Because delegators essentially _only_ interact with other application-defined
87
+ units, they operate at a single [level of abstraction](#level-of-abstraction)
88
+ and make it easier for each unit to which they delegate to also operate at
89
+ a single level of abstraction
90
+
91
+ To illustrate, a dependency graph resulting from outside-in isolated TDD will
92
+ often look like this, with only as many layers of delegators as are necessary to
93
+ identify single-purpose units to implement domain logic:
94
+
95
+ <p align="center" width="100%">
96
+ <img src="../img/delegator_tree.png" width="320" alt="a tree of dependencies with logic as the leaf nodes">
97
+ </p>
98
+
99
+ ## Demonstration
100
+
101
+ Mocktail's API was designed so that you could configure a [stubbing](#stub) or
102
+ [spy verification](#spy) by invoking a mocked [dependency](#dependency)'s method
103
+ _exactly_ as the subject would. This has two benefits: reducing the mental
104
+ overhead of keeping a larger mocking API straight and increasing the
105
+ find-and-replaceability of your code. We call that invocation used to configure
106
+ the mock a "demonstration", because the test is demonstrating how it expects
107
+ the mocked method to be called.
108
+
109
+ For example, in the following stubbing:
110
+
111
+ ```ruby
112
+ stubs { @highball.stir(times: 3) }.with { :bubbles }
113
+ ```
114
+
115
+ The mock `@highball` has its `stir` method stubbed with the return value
116
+ `:bubbles` when it is invoked as demonstrated above.
117
+
118
+ Or this verification:
119
+
120
+ ```ruby
121
+ verify { @highball.drink!(speed: SIPPING) }
122
+ ```
123
+
124
+ In this case, `@highball.drink!(speed: SIPPING)` is a demonstration of the call
125
+ we expect to the subject to invoke.
126
+
127
+
128
+ ## Dependency
129
+
130
+ In [isolated unit testing](#isolated-unit-testing), a "dependency" almost always
131
+ refers to a plain ol' Ruby class for which one or more instances are depended on
132
+ by a [subject under test](#subject-under-test).
133
+
134
+ This usage of the word "dependency" in the context of unit testing with mocking
135
+ libraries stands in contrast to most others, where the word most often refers to
136
+ third-party libraries and frameworks (usually distributed as Ruby gems) or to
137
+ networked services (e.g. an HTTP API). In this use, an integrated application or
138
+ project is implied as the thing depending on the dependency.
139
+
140
+ In Gerard Mezsaros' XUnit Patterns, he referred to dependencies less ambiguously
141
+ as [depended-on components (DOC)](http://xunitpatterns.com/DOC.html).
142
+
143
+ ## Isolated unit testing
144
+
145
+ Isolated unit testing (also known as "mockist", "London-school" test-driven
146
+ development, or discovery testing) was most thoroughly defined in Steve Freeman
147
+ and Nat Pryce's book [Growing Object-Oriented Software, Guided by
148
+ Tests](https://www.amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/0321503627).
149
+ Whereas traditional test-driven development often builds systems "bottom-up" by
150
+ starting with units that implement domain logic (leaving the ultimate
151
+ composition of dependencies up to the individual to compose or extract
152
+ manually), isolated TDD starts "outside-in" and it results in the decomposition
153
+ of a big problem into small units as a matter of course.
154
+
155
+ In simplest terms, an isolated unit test exercises the behavior of the
156
+ [subject](#subject-under-test) but not of any of its dependencies, instead
157
+ replacing all of them at runtime with alternatives controlled by the test. This
158
+ puts the subject under extreme isolation, allowing the tester to:
159
+
160
+ * Test the subject's behavior without invoking the behavior of its
161
+ [dependencies](#dependency) and thereby introducing a transitive dependency on
162
+ them
163
+ * Assert or observe the subject's behavior directly, as opposed to measuring
164
+ the a return value or a side effect of a dependency
165
+ * validate the design of the contracts between the subject and its dependencies
166
+ by responding to the pain of stubbing and verifying any interactions with the
167
+ dependencies, because (assuming an expressive mocking library) API contracts
168
+ that are hard to fake are generally also hard to use
169
+
170
+ Code that results from isolated test-driven development tends to result in
171
+ separate classes of units: one that breaks down the work
172
+ ([delegators](#delegator)) and another that implements a single aspect of the
173
+ work work as single-purpose units. This approach is typified by tree-shaped
174
+ designs that branch from the program's entry point into a set of delegators and
175
+ a larger number of implementation objects as leaf nods—many of them behaving as
176
+ pure functions.
177
+
178
+ ## Level of Abstraction
179
+
180
+ The phrase "level of abstraction" is both ironically and inherently amorphous,
181
+ because it refers to a conceptual [layer of
182
+ concepts](https://en.wikipedia.org/wiki/Abstraction_layer). The term is most
183
+ often used in the context of the exhortation "don't mix levels of abstraction",
184
+ which is even harder to pin down a meaning to.
185
+
186
+ To clarify the term for the purpose of a discussion of [isolated unit
187
+ testing](#isolated-unit-testing), consider a level of abstraction as "the
188
+ classification of things a unit of code interacts with". For example, imagine
189
+ units of code as interacting with one or more of these "levels" of abstraction:
190
+
191
+ 1. Primitive booleans, strings, numbers
192
+ 2. Collections of multiple primitives
193
+ 3. Value objects containing primitives and collections
194
+ 4. Implementers of domain logic that operate on [value objects](#value)
195
+ 5. [Delegators](#delegator) of implementers of domain logic
196
+
197
+ When practicing outside-in test-driven development, testing
198
+ [subjects](#subject-under-test) operating on that 5th level of abstraction is
199
+ the only layer for which mocking out dependencies has a clear, valuable purpose.
200
+ Moreover, if such a subject _also_ interacts significantly with layers 1-4, the
201
+ test will generally be painful to write, because the [test double](#test-double)
202
+ configuration will be more complicated. As a result, practicing outside-in TDD
203
+ and responding to testing pain by adjusting the design of the production code
204
+ will often result in delegator objects that do nothing but delegate their work
205
+ up to other units that implement domain logic as opposed to _mixing levels
206
+ of abstraction_ in their implementation.
207
+
208
+ ## Mock
209
+
210
+ A mock is a specific type of [test double](#test-double) that is typified by
211
+ having any expected invocations specified _in advance_ of invoking the
212
+ [subject](#subject-under-test) (that is, during the [arrange
213
+ phase](#arrange-act-assert), as opposed to the assertion phase). Typically, a
214
+ mock would be configured, the subject invoked, and then the mock would be called
215
+ to verify that its expectations were met.
216
+
217
+ Mocktail creates test doubles that perform validation as [spies](#spy), but
218
+ Minitest ships with `Minitest::Mock`, which implements traditional mocking
219
+ behavior:
220
+
221
+ ```ruby
222
+ mock = Minitest::Mock.new
223
+ mock.expect(:some_method, :some_arg)
224
+
225
+ subject.do_stuff(mock)
226
+
227
+ mock.verify
228
+ # => if `mock.some_method(:some_arg)` is not called, will raise:
229
+ # MockExpectationError: "expected some_method() => :some_arg"
230
+ ```
231
+
232
+ Mock objects were popularized in the [Extreme
233
+ Programming](https://en.wikipedia.org/wiki/Extreme_programming) community by
234
+ Steve Freeman and others, with their paper on what they termed
235
+ "[Endotesting](https://www2.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF)" at the XP 2000 conference.
236
+ Mocking was the dominant method of verifying behavior with test doubles until
237
+ 2010, as [test spies](#spy) began to pick up more steam, thanks to the
238
+ popularity of Java's [Mockito](https://site.mockito.org) and
239
+ [Jasmine](https://jasmine.github.io/api/edge/global.html#spyOn) in JavaScript.
240
+
241
+ ## Partial mock
242
+
243
+ A partial mock is a [test double](#test-double) that replaces some, but not all,
244
+ of its real functionality with fake functionality. This could refer to its
245
+ state, behavior, or some combination of both. Partial mocks are generally
246
+ considered an antipattern, for several reasons:
247
+
248
+ * [Isolated tests](#isolated-unit-testing) are designed to establish clear boundaries between a
249
+ [subject](#subject-under-test) and its [dependencies](#dependency), so drawing
250
+ that border somewhere in the middle of one of the dependencies represents an
251
+ inherently unclear boundary
252
+ * Whenever a real method on a partial mock calls a fake method on itself, the
253
+ test author needs to concern themselves with any stubbing and verifying
254
+ happening within the internals of a dependency (as opposed to the subject being
255
+ tested itself), and because those implementation details can change at any time,
256
+ the test of an unrelated subject that is facially isolated from the partial mock
257
+ could very likely fail
258
+ * It is perhaps not-so-surprisingly very easy for internal state held by partial
259
+ mocks to enter undefined states, leading to behavior that's completley unlike
260
+ how their "real" methods will behave in production, so their perceived realness
261
+ is often illusory
262
+
263
+ Even when things are kept very simple, mocking is poorly understood and leads to
264
+ a lot of confusion, so the increase in complexity represented by partial mocks
265
+ results in test-scoped code that's very difficult to read and understand how it
266
+ is behaving and what that behavior _means_ in terms of what assurances are being
267
+ provided by the test.
268
+
269
+ ## Proxy
270
+
271
+ In the context of mocking, the word proxy most often describes a [test
272
+ doubles](#test-double) that records all interactions made against its methods
273
+ (like a [spy](#spy)), but unlike every other kind of test double, proceeds to
274
+ call through to the _actual_ implementation of the dependency. This can be seen
275
+ as having the best of both worlds (verifying interactions without violating
276
+ their veracity), but more often results in tests make unnecessarily many
277
+ assertions and promotes the design of code that overly relies on side effects
278
+ over pure functions.
279
+
280
+ Proxies aren't especially common in mocking libraries, but can be found in rr's
281
+ [mock.proxy](https://github.com/rr/rr/blob/master/doc/03_api_overview.md#mockproxy)
282
+ API and, in JavaScript, with Jasmine spies'
283
+ [callThrough()](https://jasmine.github.io/api/edge/SpyStrategy.html) function.
284
+
285
+
286
+ ## Spy
287
+
288
+ A spy is a special sub-type of a [test double](#test-double) that describes a
289
+ fake object that silently records all invocations made against it and provides a
290
+ way for a test to interrogate those interactions in the
291
+ [assert](#arrange-act-assert) phase. The term "[test
292
+ spy](http://xunitpatterns.com/Test%20Spy.html)" was first coined by Gerard
293
+ Mezsaros for his book [XUnit
294
+ Patterns](https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054/).
295
+
296
+ The "mock" methods created by Mocktail can behave as both spies and
297
+ [stubs](#stub), as they allow after-the-fact assertion and introspection via the
298
+ [verify](../support/api.md#mocktailverify) DSL method. (This approach also
299
+ enabled debugging utilities like
300
+ [Mocktail.explain](../support/api.md#mocktailexplain) and
301
+ [Mocktail.calls](../support/api.md#mocktailcalls).)
302
+
303
+ In general, interactions should only be verified explicitly when the
304
+ [dependency](#dependency) triggers a side effect that doesn't return a
305
+ meaningful value that is able to be observed by the
306
+ [subject](#subject-under-test)'s ultimate result. In such a case, a spy
307
+ is used like this:
308
+
309
+ ```ruby
310
+ copy_machine = Mocktail.of(CopyMachine)
311
+ subject = Accountant.new
312
+
313
+ subject.record(:secret_stuff, with: copy_machine)
314
+
315
+ verify { copy_machine.copy(:secret_stuff) }
316
+ # => if `copy_machine.copy` was invoked with `:secret_stuff`, nothing happens.
317
+ # Otherwise, it raises Mocktail::VerificationError:
318
+ #
319
+ # Expected mocktail of `CopyMachine#copy' to be called like:
320
+ #
321
+ # copy(:secret_stuff)
322
+ #
323
+ # But it was never called.
324
+ ```
325
+
326
+ ## Stub
327
+
328
+ In mocking parlance, a "stub" is a specific type of a [test
329
+ double](#test-double) wherein a function or method is configured in the
330
+ [arrange](#arrange-act-assert) phase of an [isolated
331
+ test](#isolated-unit-testing) to respond with a particular value (or raise a
332
+ particular error). A particular stubbing (that is, a preconfigured response)
333
+ might be applied to all invocations of the stubbed method, or made to depend on
334
+ the arguments provided.
335
+
336
+ The purpose of stubbing is to facilitate downstream behavior in the
337
+ [subject](#subject-under-test) by configuring an artificial response (return
338
+ values or errors) in a test double of a [dependency](#dependency). Mocktail's
339
+ "mocks" can be used as both stubs and [spies](#spy), so here's an example
340
+ using its [stubs](api.md#mocktailstubs) DSL method:
341
+
342
+ ```ruby
343
+ peeler = Mocktail.of(Peeler)
344
+ subject = Bartender.new(peeler)
345
+ stubs { peeler.peel(:orange) }.with { :peeled_orange }
346
+ stubs { peeler.peel(:lemon) }.with { :peeled_lemon }
347
+
348
+ result = subject.prep([:orange, :lemon])
349
+
350
+ assert_equal [:peeled_orange, :peeled_lemon], result
351
+ ```
352
+
353
+ In the example above, the two `stubs` merely enable the `Bartender` subject to
354
+ do its job (of invoking `peel` on each element passed to it). Because the stubs'
355
+ configuration will only return the `:peeled_` symbols if passed the correct
356
+ argument, no explicit assertion that the calls occurred is necessary. Since
357
+ calling `peeler.peel(:grape)` would return nil, the above stubbing is sufficient
358
+ to verify the dependency was invoked appropriately.
359
+
360
+ ## Subject under test
361
+
362
+ The [subject under test](http://xunitpatterns.com/SUT.html) (or "subject") was
363
+ coined by Gerard Meszaros in his book [XUnit
364
+ Patterns](https://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054/)
365
+ to refer to the _thing being tested_. That's all. Nothing too fancy!
366
+
367
+ In order to promote easier extract refactors, some developers like to assign the
368
+ subject under test to a variable name like `@subject` in every test so that test
369
+ cases can be moved between file listings with less effort. It has the added
370
+ benefit of always clarifying the thing being tested from any other
371
+ [dependencies](#dependency) referenced in the test.
372
+
373
+ ## Test double
374
+
375
+ A test double is a catch-all term for a fake object meant to stand-in for a real
376
+ thing. The name is meant to evoke the image of a stunt double who stands in for
377
+ the real actor in your tests. It was coined by Gerard Meszaros in his [book
378
+ XUnit patterns](http://xunitpatterns.com/Test Double.html). Technically, the
379
+ mocks generated by Mocktail would be most correctly described not as mocks at
380
+ all, but as "combination stubs and spies" in proper parlance, but outside a very
381
+ tiny group of people who write mocking libraries, the distinctions have turned
382
+ out to not be sufficiently meaningful to teach people half a dozen special words
383
+ for what everyone colloquially prefers to call a "mock".
384
+
385
+ ([Test Double](https://testdouble.com) is also the name of a
386
+ pretty great software consultancy with strong ties to the Ruby community and
387
+ which incidentally created and maintains Mocktail.)
388
+
389
+ ## Value
390
+
391
+ Similar to how [isolated TDD](#isolated-unit-testing) tends to lead to a sharp
392
+ distinction between [delegators](#delegator) that break up the work and small
393
+ units to implement it, struct-like objects to represent values also emerge as
394
+ distinct from objects that contain domain logic that implements features.
395
+
396
+ Because delegators typically only hold references to their
397
+ [dependencies](#dependency) and are otherwise stateless, their public methods
398
+ tend to receive value objects. Values essentially become the
399
+ "unit of work", and are passed elsewhere, wrapped, transformed, or mutated, and
400
+ returned. Values typically contain primitive data (e.g. booleans, strings, etc.),
401
+ other values, and methods that merely _elucidate_ the data they hold as opposed
402
+ to implementing feature logic.
403
+
404
+ In Ruby, values are most often implemented as
405
+ [Struct](https://docs.ruby-lang.org/en/master/Struct.html),
406
+ [Data](https://docs.ruby-lang.org/en/master/Data.html), or (in Sorbet)
407
+ [T::Struct](https://sorbet.org/docs/tstruct).
408
+
409
+ ## Wrapper object
410
+
411
+ A wrapper object, sometimes referred to as an adapter (or even "[scar
412
+ tissue](https://www.destroyallsoftware.com/talks/boundaries)") is often
413
+ introduced to wrap code whose API can't be readily changed in response to being
414
+ difficult to mock out in a test (e.g. a third-party library, a utility
415
+ maintained by another team, etc). Wrappers typically act as a solitary
416
+ chokepoint for an application's use of a third-party API, which can serve a
417
+ couple of key benefits:
418
+
419
+ * Wrappers self-document the extent to which a codebase uses a particular
420
+ dependency and make it easy to assess swapping it for an alternative without
421
+ requiring changes to be made throughout the codebase
422
+ * If a wrapper is written around a hard-to-mock (and therefore hard-to-use)
423
+ third-party API, then the wrapper can effectively serve as a rug under which the
424
+ complexity of that API can be swept by exposing its behavior through
425
+ easier-to-mock (and therefore easier-to-use) method signatures and return values
426
+ that look and feel similar to those found in the rest of the codebase
@@ -0,0 +1,44 @@
1
+ # Mocking class and module methods
2
+
3
+ Usually, Mocktail is used to create mock _instances_ of classes and modules, but
4
+ you may occassionally want or need to mock out the methods defined on a class or
5
+ module.
6
+
7
+ To fake out all the methods on a type, you can simply pass it to
8
+ [Mocktail.replace](/docs/support/api.md#mocktailreplace). Here is a little
9
+ example:
10
+
11
+ ```ruby
12
+ module Substitution
13
+ def self.for(ingredient)
14
+ ingredient.alternatives.first
15
+ end
16
+ end
17
+
18
+ # Somewhere in a test
19
+ Mocktail.replace(Substitution)
20
+
21
+ stubs { Substitution.for(:peychauds_bitters) }.with { :angosutra_bitters }
22
+
23
+ Substitution.for(:peychauds_bitters)
24
+ => :angosutra_bitters
25
+ ```
26
+
27
+ ## Heads up!
28
+
29
+ `Mocktail.replace` is a spicy operation because it globally mutates that class
30
+ or module by overwriting its methods. When called, Mocktail does its best to
31
+ dispatch the real or the fake method based on whether the currently-running
32
+ thread has faked the type, but this isn't 100% fool-proof, so be wary of
33
+ potential pollution if you use [thread-based test
34
+ parallelization](https://edgeguides.rubyonrails.org/testing.html#parallel-testing-with-threads),
35
+ and remember to configure an after-each hook to call
36
+ [Mocktail.reset](/docs/support/api.md#mocktailreset) to restore state.
37
+
38
+ ## Way to stay classy
39
+
40
+ Where to go from here?
41
+
42
+ **Head back onto the golden path and use Mocktail to create [fake instances of Ruby classes](./poro.md).**
43
+
44
+ **Put these overridden classes and modules to good use by [stubbing and verifying their methods](../stubbing_and_verifying.md).**