rspec_magic 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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 Алексей Фортуна