rspec_magic 0.1.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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +26 -0
  3. data/.rspec +3 -0
  4. data/.yardopts +7 -0
  5. data/Gemfile +8 -0
  6. data/Gemfile.lock +36 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README-ru.md +328 -0
  9. data/README.md +328 -0
  10. data/Rakefile +15 -0
  11. data/lib/rspec_magic/config.rb +17 -0
  12. data/lib/rspec_magic/stable/alias_method.rb +26 -0
  13. data/lib/rspec_magic/stable/context_when.rb +101 -0
  14. data/lib/rspec_magic/stable/described_sym.rb +62 -0
  15. data/lib/rspec_magic/stable/use_letset.rb +95 -0
  16. data/lib/rspec_magic/stable/use_method_discovery.rb +75 -0
  17. data/lib/rspec_magic/stable.rb +13 -0
  18. data/lib/rspec_magic/unstable/include_dir_context.rb +39 -0
  19. data/lib/rspec_magic/unstable.rb +13 -0
  20. data/lib/rspec_magic/version.rb +5 -0
  21. data/lib/rspec_magic.rb +11 -0
  22. data/rspec_magic.gemspec +17 -0
  23. data/spec/lib/rspec_magic/alias_method_spec.rb +19 -0
  24. data/spec/lib/rspec_magic/context_when_spec.rb +45 -0
  25. data/spec/lib/rspec_magic/described_sym_spec.rb +39 -0
  26. data/spec/lib/rspec_magic/include_dir_context/README.md +2 -0
  27. data/spec/lib/rspec_magic/include_dir_context/_context.rb +4 -0
  28. data/spec/lib/rspec_magic/include_dir_context/app/_context.rb +4 -0
  29. data/spec/lib/rspec_magic/include_dir_context/app/idc_consumer_spec.rb +15 -0
  30. data/spec/lib/rspec_magic/include_dir_context/app/models/_context.rb +4 -0
  31. data/spec/lib/rspec_magic/include_dir_context/app/models/idc_consumer_spec.rb +15 -0
  32. data/spec/lib/rspec_magic/include_dir_context/idc_consumer_spec.rb +15 -0
  33. data/spec/lib/rspec_magic/use_letset_spec.rb +134 -0
  34. data/spec/lib/rspec_magic/use_method_discovery_spec.rb +24 -0
  35. data/spec/spec_helper.rb +9 -0
  36. data/spec/support/self.rb +6 -0
  37. data/spec/support/simplecov.rb +11 -0
  38. metadata +80 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 04d4ef834d4220f6b1aa7de8211f9f7c8aa1dc1b
4
+ data.tar.gz: ef3362accf8c7ebea8f7433b5f45e28d972c4800
5
+ SHA512:
6
+ metadata.gz: b5433ad24ae7fc8b4e7899e42ee544066f0e0848f1a652d9f7c53e34ab002012aadcc94de4e037a6033b1859d04a5ded5e91315aade3cc6ce81f380627af3051
7
+ data.tar.gz: 2ace60aec6d6376e0077045d1e023a131f7688e04520e72cacf32818583ca66fd50ff166a51c9d0eb7440017580a3421bd6435e0aaae26b0c21d8a1fe3bbb9af
data/.gitignore ADDED
@@ -0,0 +1,26 @@
1
+
2
+ # General Ruby, sorted by first letter.
3
+ /.bundle/
4
+ /coverage/
5
+ /doc/
6
+ /vendor/bundle/
7
+ /.yardoc/
8
+
9
+ # Project-specific.
10
+ /*.gem
11
+
12
+ # Old etc.
13
+ *.old*
14
+ *.orig
15
+ /*.patch
16
+ *.ref*
17
+
18
+ # Nonpersistent stuff.
19
+ TODO.md
20
+
21
+ # Shared VS Code settings.
22
+ /.vscode/*
23
+ !/.vscode/*.json
24
+
25
+ # Custom VS Code settings.
26
+ /*.code-workspace
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+
2
+ -fd
3
+ --require spec_helper
data/.yardopts ADDED
@@ -0,0 +1,7 @@
1
+
2
+ --default-return void
3
+
4
+ {lib}/**/*.rb
5
+ -
6
+ README.md
7
+ README-ru.md
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+
2
+ source "https://rubygems.org"
3
+
4
+ group :development do
5
+ gem "rspec"
6
+ gem "simplecov", require: false
7
+ gem "yard"
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,36 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.5.0)
5
+ docile (1.3.5)
6
+ json (2.6.3)
7
+ rspec (3.12.0)
8
+ rspec-core (~> 3.12.0)
9
+ rspec-expectations (~> 3.12.0)
10
+ rspec-mocks (~> 3.12.0)
11
+ rspec-core (3.12.2)
12
+ rspec-support (~> 3.12.0)
13
+ rspec-expectations (3.12.3)
14
+ diff-lcs (>= 1.2.0, < 2.0)
15
+ rspec-support (~> 3.12.0)
16
+ rspec-mocks (3.12.6)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.12.0)
19
+ rspec-support (3.12.1)
20
+ simplecov (0.17.1)
21
+ docile (~> 1.1)
22
+ json (>= 1.8, < 3)
23
+ simplecov-html (~> 0.10.0)
24
+ simplecov-html (0.10.2)
25
+ yard (0.9.34)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ rspec
32
+ simplecov
33
+ yard
34
+
35
+ BUNDLED WITH
36
+ 1.17.3
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2017-2024 Alex Fortuna
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README-ru.md ADDED
@@ -0,0 +1,328 @@
1
+
2
+ # Немного магии для RSpec-тестов
3
+
4
+ <!-- @import "[TOC]" {cmd="toc" depthFrom=2 depthTo=6 orderedList=false} -->
5
+
6
+ <!-- code_chunk_output -->
7
+
8
+ - [Что это?](#что-это)
9
+ - [Установка](#установка)
10
+ - [Фичи](#фичи)
11
+ - [`alias_method`](#alias_method)
12
+ - [`context_when`](#context_when)
13
+ - [`described_sym`](#described_sym)
14
+ - [`include_dir_context`](#include_dir_context)
15
+ - [`use_letset`](#use_letset)
16
+ - [`use_method_discovery`](#use_method_discovery)
17
+ - [Подробно](#подробно)
18
+ - [Про установку](#про-установку)
19
+ - [Про `context_when`](#про-context_when)
20
+ - [Про `include_dir_context`](#про-include_dir_context)
21
+ - [Copyright](#copyright)
22
+
23
+ <!-- /code_chunk_output -->
24
+
25
+ ## Что это?
26
+
27
+ 🆎 *An English version of this text is also available: [README.md](README.md).*
28
+
29
+ RSpecMagic — набор расширений для написания компактных и выразительных тестов.
30
+
31
+ ## Установка
32
+
33
+ > 💡 *Предполагается, что RSpec в нашем проекте мы уже настроили.*
34
+
35
+ Добавляем в `Gemfile`:
36
+
37
+ ```ruby
38
+ gem "rspec_magic"
39
+ #gem "rspec_magic", git: "https://github.com/dadooda/rspec_magic"
40
+ ```
41
+
42
+ Добавляем в автозагрузку RSpec (обычно это `spec/spec_helper.rb`):
43
+
44
+ ```ruby
45
+ require "rspec_magic/stable"
46
+ require "rspec_magic/unstable"
47
+
48
+ RSpecMagic::Config.spec_path = File.expand_path(".", __dir__)
49
+ ```
50
+
51
+ Настройка `spec_path` нужна для некоторых фич, например, [include_dir_context](#include_dir_context).
52
+ Вычисленный путь должен указывать на `spec/` в директории проекта.
53
+
54
+ См. [Подробно](#про-установку).
55
+
56
+ ## Фичи
57
+
58
+ ### `alias_method`
59
+
60
+ Matcher, сверяющий, что метод является alias'ом другого метода.
61
+
62
+ ```ruby
63
+ describe User do
64
+ it { is_expected.to alias_method(:admin?, :is_admin) }
65
+ end
66
+ ```
67
+
68
+ ### `context_when`
69
+
70
+ Создаём стереотипный контекст, задающий внутри себя одну или несколько `let`-переменных.
71
+ Блоки ниже взаимозаменяемы.
72
+
73
+ ```ruby
74
+ context_when name: "Joe", age: 25 do
75
+ it do
76
+ expect([name, age]).to eq ["Joe", 25]
77
+ end
78
+ end
79
+ ```
80
+
81
+ ```ruby
82
+ context "when { name: \"Joe\", age: 25 }" do
83
+ let(:name) { "Joe" }
84
+ let(:age) { 25 }
85
+ it do
86
+ expect([name, age]).to eq ["Joe", 25]
87
+ end
88
+ end
89
+ ```
90
+
91
+ См. [Подробно](#про-context_when).
92
+
93
+ ### `described_sym`
94
+
95
+ `described_sym` и `me` — представление имени `described_class` в виде `Symbol`.
96
+ Помогает не «долдонить» мнемоническим названием тестируемого класса, например,
97
+ при создании записей с помощью factory.
98
+
99
+ ```ruby
100
+ describe UserProfile do
101
+ it { expect(described_sym).to eq :user_profile }
102
+ it { expect(me).to eq :user_profile }
103
+ end
104
+ ```
105
+
106
+ С factory:
107
+
108
+ ```ruby
109
+ describe UserProfile do
110
+ let(:uprof1) { create described_sym }
111
+ let(:uprof2) { create me }
112
+
113
+ end
114
+ ```
115
+
116
+ ### `include_dir_context`
117
+
118
+ ♒︎ *Эта фича добавлена недавно и может измениться.*
119
+
120
+ Организуем общие контексты ([shared_context](https://rspec.info/features/3-12/rspec-core/example-groups/shared-context/)) в иерархии.
121
+ Автоматически включаем нужные общие контексты в наш тест.
122
+
123
+ Шаги:
124
+
125
+ 1. Убеждаемся, что в настройках правильно прописана `RSpecMagic::Config.spec_path`.
126
+ Она должна указывать на `spec/`.
127
+
128
+ 2. По файловому дереву тестов создаём файлы общих контекстов *с одинаковым именем,* например, `_context.rb`.
129
+ Содержимое `_context.rb` всегда имеет вид:
130
+
131
+ ```ruby
132
+ shared_context __dir__ do
133
+
134
+ end
135
+ ```
136
+
137
+ 3. Добавляем в условный `spec_helper.rb`:
138
+
139
+ ```ruby
140
+ # Загружаем иерархию shared contexts.
141
+ Dir[File.expand_path("**/_context.rb", __dir__)].each { |fn| require fn }
142
+ ```
143
+
144
+ 4. В spec-файле добавляем вызов `include_dir_context` в тело главного `describe`:
145
+
146
+ ```ruby
147
+ describe … do
148
+ include_dir_context __dir__
149
+
150
+ end
151
+ ```
152
+
153
+ Например, наш spec-файл это `spec/app/controllers/api/player_controller_spec.rb`.
154
+
155
+ В главный `describe` будут последовательно загружены, *если они есть,* контексты из файлов:
156
+
157
+ ```
158
+ spec/_context.rb
159
+ spec/app/_context.rb
160
+ spec/app/controllers/_context.rb
161
+ spec/app/controllers/api/_context.rb
162
+ ```
163
+
164
+ См. [Подробно](#про-include_dir_context).
165
+
166
+ ### `use_letset`
167
+
168
+ Создаём на уровне `describe` метод для задания `let`-переменных, автоматически составляющих коллекцию типа `Hash`.
169
+
170
+ ```ruby
171
+ describe do
172
+ # Метод -- `let_a`. Коллекция -- `attrs`.
173
+ use_letset :let_a, :attrs
174
+
175
+ # Декларируем переменные, которые составляют коллекцию `attrs`.
176
+ let_a(:age)
177
+ let_a(:name)
178
+
179
+ subject { attrs }
180
+
181
+ # Ни одна переменная пока не задана, поэтому коллекция будет пустой.
182
+ it { is_expected.to eq({}) }
183
+
184
+ # Задаём `name` и видим его в коллекции.
185
+ context_when name: "Joe" do
186
+ it { is_expected.to eq(name: "Joe") }
187
+
188
+ # Задаём `age` и видим обе переменные в коллекции.
189
+ context_when age: 25 do
190
+ it { is_expected.to eq(name: "Joe", age: 25) }
191
+ end
192
+ end
193
+ end
194
+ ```
195
+
196
+ Если передан блок, `let_a` работает как обычный `let`. Такой вариант изредка тоже бывает полезен:
197
+
198
+ ```ruby
199
+ describe do
200
+ use_letset :let_a, :attrs
201
+
202
+ let_a(:age) { 25 }
203
+ let_a(:name) { "Joe" }
204
+
205
+ it { expect(attrs).to eq(name: "Joe", age: 25) }
206
+ end
207
+ ```
208
+
209
+ ### `use_method_discovery`
210
+
211
+ Создаём автоматическую `let`-переменную, содержащую имя метода или action,
212
+ вычисленное из текста вышестоящего `describe`.
213
+
214
+ ```ruby
215
+ describe do
216
+ use_method_discovery :m
217
+
218
+ subject { m }
219
+
220
+ describe "#first_name" do
221
+ it { is_expected.to eq :first_name }
222
+ end
223
+
224
+ describe ".some_stuff" do
225
+ it { is_expected.to eq :some_stuff }
226
+ end
227
+
228
+ describe "GET some_action" do
229
+ describe "intermediate context" do
230
+ it { is_expected.to eq :some_action } # (1)
231
+ end
232
+ end
233
+ end
234
+ ```
235
+
236
+ `m` находит ближайший *подходящий* контекст, формат текста которого допускает выемку имени метода.
237
+ См. (1) — `m` пропустила вольно отформатированный `"intermediate context"` и сработала
238
+ на `"GET some_action"`.
239
+
240
+ ## Подробно
241
+
242
+ ### Про установку
243
+
244
+ 1. `stable` и `unstable` — наборы фич. В набор `unstable` входят фичи,
245
+ добавленные недавно. Они могут измениться в следующих версиях.
246
+
247
+ 2. Можно включить только конкретные фичи. Например:
248
+
249
+ ```ruby
250
+ require "rspec_magic/stable/use_method_discovery"
251
+ ```
252
+
253
+ ### Про `context_when`
254
+
255
+ 1. Контекст можно исключить из обработки, приписав к началу `x`:
256
+
257
+ ```ruby
258
+ xcontext_when … do
259
+
260
+ end
261
+ ```
262
+
263
+ 2. Можно определить свой метод форматирования строки для отчёта:
264
+
265
+ ```ruby
266
+ describe "…" do
267
+ def self._context_when_formatter(h)
268
+ "when #{h.to_json}"
269
+ end
270
+
271
+ context_when … do
272
+
273
+ end
274
+ end
275
+ ```
276
+
277
+ 3. `context_when` эффективно работает в паре с [use_letset](#use_letset),
278
+ обычно для задания атрибутов тестируемого объекта.
279
+
280
+ 4. Значения `let`-переменных вычисляются на уровне `describe`.
281
+ Если нужны значения, вычисляемые на уровне `it`, следует использовать обычный `let(…) { … }` внутри контекста.
282
+
283
+ ### Про `include_dir_context`
284
+
285
+ Есть в RSpec классная штука — общие контексты, они же [shared_context](https://rspec.info/features/3-12/rspec-core/example-groups/shared-context/).
286
+ Замысел простой: где-то (в условном `spec_helper.rb`) мы создаём нечто общее через `shared_context "то сё"`,
287
+ а потом через `include_context "то сё"` импортируем материал в нужный там тест.
288
+
289
+ Наполнять `shared_context` можно чем угодно — общими тестами, `let`-переменными, *но главное* —
290
+ методами уровня `describe` (`def self.doit`) и методами уровня `it` (`def doit`).
291
+
292
+ Чем не библиотека?
293
+
294
+ Казалось бы — вот оно счастьюшко, сочиняй свои расширения, разноси по общим контекстам, где надо импортируй и радуйся.
295
+ Но есть неприятная особенность: штатные средства организации контекстов очень примитивны и опираются на глобальные уникальные имена.
296
+
297
+ RSpec не позволяет организовывать общие контексты в иерархии,
298
+ чтобы автоматически импортировать контексты-«библиотеки» в группы spec-файлов, как то:
299
+ *всем* тестам моделей — одно, *всем* тестам контроллеров — другое, а *всем* им вместе — третье.
300
+
301
+ Чтобы поддерживать мало-мальский порядок, приходится натужно придумывать общим контекстам
302
+ уникальные имена, и *в каждом* spec-файле перечислять импортируемое унылым повторяющимся списком:
303
+
304
+ ```ruby
305
+ describe … do
306
+ include_context "basic"
307
+ include_context "controllers"
308
+ include_context "api_controllers"
309
+
310
+ end
311
+ ```
312
+
313
+ И это ещё продвинутый уровень.
314
+ Чаще всего даже так не делают, а просто сваливают все расширения в кучу и включают сразу всё,
315
+ просто потому, что «некогда разбираться».
316
+
317
+ Что даёт `include_dir_context`?
318
+
319
+ 1. Возможность организовывать общие контексты в иерархии.
320
+ 2. Возможность автоматически включать наборы *того, что нужно, туда, куда нужно.*
321
+
322
+ Как это делать, описано в [основной главе](#include_dir_context).
323
+
324
+ ## Copyright
325
+
326
+ Продукт распространяется свободно на условиях лицензии MIT.
327
+
328
+ — © 2017-2024 Алексей Фортуна