spec_forge 0.1.0 → 0.3.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -2
  3. data/README.md +366 -143
  4. data/flake.nix +2 -2
  5. data/lib/spec_forge/attribute/chainable.rb +19 -16
  6. data/lib/spec_forge/attribute/factory.rb +6 -18
  7. data/lib/spec_forge/attribute/faker.rb +56 -20
  8. data/lib/spec_forge/attribute/literal.rb +4 -0
  9. data/lib/spec_forge/attribute/resolvable.rb +4 -6
  10. data/lib/spec_forge/attribute/resolvable_array.rb +4 -0
  11. data/lib/spec_forge/attribute/resolvable_hash.rb +4 -0
  12. data/lib/spec_forge/attribute/variable.rb +6 -13
  13. data/lib/spec_forge/attribute.rb +3 -12
  14. data/lib/spec_forge/cli/init.rb +2 -9
  15. data/lib/spec_forge/configuration.rb +58 -0
  16. data/lib/spec_forge/factory.rb +4 -4
  17. data/lib/spec_forge/http/backend.rb +42 -8
  18. data/lib/spec_forge/http/client.rb +2 -2
  19. data/lib/spec_forge/http/request.rb +11 -14
  20. data/lib/spec_forge/normalizer/configuration.rb +77 -0
  21. data/lib/spec_forge/normalizer/expectation.rb +1 -0
  22. data/lib/spec_forge/normalizer/spec.rb +1 -0
  23. data/lib/spec_forge/normalizer.rb +14 -11
  24. data/lib/spec_forge/runner.rb +98 -72
  25. data/lib/spec_forge/spec/expectation/constraint.rb +2 -5
  26. data/lib/spec_forge/spec/expectation.rb +32 -13
  27. data/lib/spec_forge/spec.rb +20 -14
  28. data/lib/spec_forge/version.rb +1 -1
  29. data/lib/spec_forge.rb +20 -11
  30. data/lib/templates/forge_helper.tt +48 -0
  31. data/spec_forge/forge_helper.rb +37 -0
  32. data/spec_forge/specs/users.yml +6 -4
  33. metadata +7 -8
  34. data/lib/spec_forge/config.rb +0 -84
  35. data/lib/spec_forge/environment.rb +0 -71
  36. data/lib/spec_forge/normalizer/config.rb +0 -104
  37. data/lib/templates/config.tt +0 -19
  38. data/spec_forge/config.yml +0 -19
data/README.md CHANGED
@@ -1,18 +1,17 @@
1
1
  # SpecForge
2
2
 
3
- **Please note: This gem is under active development and isn't quite ready for use**
4
-
5
- I have 98% of the first release done, but I still have a lot of testing and polishing to ensure it works as expected.
6
-
7
- ---
3
+ [![Gem Version](https://badge.fury.io/rb/spec_forge.svg)](https://badge.fury.io/rb/spec_forge)
4
+ [![Tests](https://github.com/itsthedevman/spec_forge/actions/workflows/main.yml/badge.svg)](https://github.com/itsthedevman/spec_forge/actions/workflows/main.yml)
5
+ ![Ruby Version](https://img.shields.io/badge/ruby-3.3.6-ruby)
8
6
 
9
7
  Write API tests in YAML that read like documentation:
10
8
 
11
9
  ```yaml
12
10
  user_profile:
13
11
  path: /users/1
14
- expect:
15
- status: 200
12
+ expectations:
13
+ - expect:
14
+ status: 200
16
15
  json:
17
16
  name: kind_of.string
18
17
  email: /@/
@@ -20,7 +19,38 @@ user_profile:
20
19
 
21
20
  That's a complete test. No Ruby code, no configuration files, no HTTP client setup - just a clear description of what you're testing. Under the hood, you get all the power of RSpec's matchers, Faker's data generation, and FactoryBot's test objects.
22
21
 
23
- But that's just scratching the surface.
22
+ ## Why SpecForge?
23
+
24
+ SpecForge shines when you need:
25
+
26
+ 1. **Accessible API Testing**: Non-developers can write and maintain tests without Ruby knowledge. The YAML syntax reads like documentation.
27
+ 2. **Living Documentation**: Tests serve as clear, maintainable documentation of your API's expected behavior.
28
+ 3. **Power Without Complexity**: Get the benefits of Ruby-based tests (dynamic data, factories, matchers) without writing Ruby code.
29
+ 4. **Quick Setup**: Start testing APIs without configuring HTTP clients or writing boilerplate code.
30
+ 5. **Gradual Adoption**: Use alongside your existing test suite. Keep complex tests in RSpec while making simple API tests more accessible.
31
+
32
+ ## When Not to Use SpecForge
33
+
34
+ Consider alternatives when you need:
35
+
36
+ 1. **Complex Ruby Logic**: If your tests require custom Ruby code for data transformations or validations.
37
+ 2. **Complex Test Setup**: When you need intricate database states or specific service mocks.
38
+ 3. **Custom Response Validation**: For validation logic beyond what matchers provide.
39
+ 4. **Complex Non-JSON Testing**: While SpecForge handles basic XML/HTML responses (coming soon), complex validation might need specialized tools.
40
+
41
+ ## Roadmap
42
+
43
+ Current development priorities:
44
+ - [ ] Support for running individual specs
45
+ - [ ] Array support for `json` expectations
46
+ - [ ] Negated matchers: `matcher.not`
47
+ - [ ] `create_list/build_list` factory strategies
48
+ - [ ] `transform.map` support
49
+ - [ ] Improved error handling
50
+ - [ ] XML/HTML response handling
51
+ - [ ] OpenAPI generation from tests
52
+
53
+ Have a feature request? Open an issue on GitHub!
24
54
 
25
55
  ## Table of Contents
26
56
 
@@ -30,33 +60,36 @@ But that's just scratching the surface.
30
60
  - [Getting Started](#getting-started)
31
61
  - [Writing Your First Test](#writing-your-first-test)
32
62
  - [Configuration](#configuration)
33
- - [Base URL](#base-url)
34
- - [Authorization](#authorization)
35
- - [The Spec Structure](#the-spec-structure)
63
+ - [Basic Configuration](#basic-configuration)
64
+ - [Framework Integration](#framework-integration)
65
+ - [Factory Configuration](#factory-configuration)
66
+ - [Debug Configuration](#debug-configuration)
67
+ - [Test Framework Configuration](#test-framework-configuration)
68
+ - [Configuration Inheritance](#configuration-inheritance)
69
+ - [Writing Tests](#writing-tests)
36
70
  - [Basic Structure](#basic-structure)
37
71
  - [Testing Response Data](#testing-response-data)
38
72
  - [Multiple Expectations](#multiple-expectations)
39
73
  - [Request Data](#request-data)
74
+ - [Path Parameters](#path-parameters)
40
75
  - [Dynamic Features](#dynamic-features)
41
76
  - [Variables](#variables)
42
77
  - [Transformations](#transformations)
43
- - [Factory Integration](#factory-integration)
78
+ - [Chaining Support](#chaining-support)
79
+ - [Factory Support](#factory-support)
80
+ - [Automatic Discovery](#automatic-discovery)
81
+ - [Custom Factory Paths](#custom-factory-paths)
82
+ - [Build Strategies](#build-strategies)
83
+ - [YAML Factory Definitions](#yaml-factory-definitions)
44
84
  - [RSpec Matchers](#rspec-matchers)
45
85
  - ["be" namespace](#be-namespace)
46
86
  - ["kind_of" namespace](#kind_of-namespace)
47
87
  - ["matchers" namespace](#matchers-namespace)
48
- - [Roadmap](#roadmap)
88
+ - [How Tests Work](#how-tests-work)
49
89
  - [Contributing](#contributing)
50
90
  - [License](#license)
51
91
  - [Looking for a Software Engineer?](#looking-for-a-software-engineer)
52
92
 
53
- ## Features
54
-
55
- - **Write Tests in YAML**: Create clear, maintainable API tests using a declarative YAML syntax
56
- - **RSpec Integration**: Harness RSpec's powerful matcher system and reporting through an intuitive interface
57
- - **Dynamic Test Data**: Generate realistic test data using Faker, transformations, and a flexible variable system
58
- - **Factory Integration**: Seamless integration with FactoryBot for test data generation
59
-
60
93
  ## Compatibility
61
94
 
62
95
  Currently tested on:
@@ -96,13 +129,7 @@ Or with bundle:
96
129
  bundle exec spec_forge init
97
130
  ```
98
131
 
99
- This creates the `spec_forge` directory with the following structure:
100
- ```
101
- spec_forge/
102
- config.yml # Global configuration
103
- factories/ # Your factory definitions
104
- specs/ # Your test specifications
105
- ```
132
+ This creates the `spec_forge` directory containing factory definitions, test specifications, and global configuration.
106
133
 
107
134
  ## Writing Your First Test
108
135
 
@@ -119,8 +146,7 @@ get_user:
119
146
  path: /users/1
120
147
  method: GET
121
148
  expectations:
122
- - name: "Retrieves a user successfully"
123
- expect:
149
+ - expect:
124
150
  status: 200
125
151
  json:
126
152
  id: 1
@@ -136,68 +162,163 @@ spec_forge run
136
162
 
137
163
  ## Configuration
138
164
 
139
- The configuration file (`spec_forge/config.yml`) supports ERB and allows you to set global options for your test suite.
165
+ ### Basic Configuration
140
166
 
141
- ### Base URL
167
+ When you initialize SpecForge, it creates a `forge_helper.rb` file in your `spec_forge` directory. This serves as your central configuration file:
142
168
 
143
- The base URL can be specified at three levels (in order of precedence):
144
- 1. Expectation level
145
- 2. Spec level
146
- 3. Config level (`config.yml`)
169
+ ```ruby
170
+ SpecForge.configure do |config|
171
+ # Base URL for all requests
172
+ config.base_url = "http://localhost:3000"
173
+
174
+ # Default headers sent with every request
175
+ config.headers = {
176
+ "Authorization" => "Bearer #{ENV.fetch("API_TOKEN", "")}",
177
+ "Accept" => "application/json"
178
+ }
179
+
180
+ # Optional: Default query parameters for all requests
181
+ config.query = {
182
+ api_key: ENV["API_KEY"]
183
+ }
184
+ end
185
+ ```
147
186
 
148
- ```yaml
149
- # config.yml
150
- base_url: https://api.example.com
187
+ ### Framework Integration
151
188
 
152
- # specs/users.yml
153
- get_user:
154
- base_url: https://staging.example.com # Overrides config.yml
155
- path: /users/1
189
+ SpecForge works seamlessly with Rails and RSpec:
190
+
191
+ ```ruby
192
+ # Rails Integration
193
+ require_relative "../config/environment"
194
+
195
+ # RSpec Integration (includes your existing configurations)
196
+ require_relative "../spec/spec_helper"
197
+
198
+ # Load custom files (models, libraries, etc)
199
+ Dir[File.join(__dir__, "..", "lib", "**", "*.rb")].sort.each { |f| require f }
200
+ ```
201
+
202
+ ### Factory Configuration
203
+
204
+ SpecForge provides flexible configuration options for working with FactoryBot factories:
205
+
206
+ ```ruby
207
+ SpecForge.configure do |config|
208
+ # Disable auto-discovery if needed (default: true)
209
+ config.factories.auto_discover = false
210
+
211
+ # Add custom factory paths (appends to default paths)
212
+ config.factories.paths += ["lib/factories"]
213
+ end
214
+ ```
215
+
216
+ ### Debug Configuration
217
+
218
+ Enable debugging by adding `debug: true` (aliases: `breakpoint`, `pry`) at either the spec or expectation level:
219
+
220
+ ```ruby
221
+ SpecForge.configure do |config|
222
+ # Custom debug handler (defaults to printing state overview)
223
+ config.on_debug { binding.pry } # Requires 'pry' gem
224
+ end
225
+ ```
226
+
227
+ ```yaml
228
+ get_users:
229
+ debug: true # Debug all expectations in this spec
230
+ path: /users
156
231
  expectations:
157
- - name: "Production check"
158
- base_url: https://prod.example.com # Overrides spec level
159
- expect:
232
+ - expect:
160
233
  status: 200
234
+ - debug: true # Debug just this expectation
235
+ expect:
236
+ status: 404
237
+ json:
238
+ error: kind_of.string
239
+ ```
240
+
241
+ When debugging, you have access to:
242
+ - `expectation` - Current expectation being validated
243
+ - `variables` - Resolved variables for the current expectation
244
+ - `request` - Request details (url, method, headers, etc.)
245
+ - `response` - Full response including headers, status, and parsed body
246
+ - `expected_status` - Expected HTTP status code
247
+ - `expected_json` - Expected JSON structure with matchers
248
+
249
+ Or call `self` from an interactive session to see everything as a hash
250
+
251
+ ### Test Framework Configuration
252
+
253
+ Access RSpec's configuration through the `specs` attribute:
254
+
255
+ ```ruby
256
+ SpecForge.configure do |config|
257
+ # Setup before all tests
258
+ config.specs.before(:suite) do
259
+ DatabaseCleaner.strategy = :truncation
260
+ DatabaseCleaner.clean_with(:truncation)
261
+ end
262
+
263
+ # Wrap each test
264
+ config.specs.around do |example|
265
+ DatabaseCleaner.cleaning do
266
+ example.run
267
+ end
268
+ end
269
+ end
161
270
  ```
162
271
 
163
- ### Authorization
272
+ ### Configuration Inheritance
164
273
 
165
- SpecForge currently supports header-based authorization. Configure it in your `config.yml`:
274
+ All configuration options can be overridden at three levels (in order of precedence):
275
+
276
+ 1. Individual expectation
277
+ 2. Spec level
278
+ 3. Global configuration (forge_helper.rb)
279
+
280
+ For example:
166
281
 
167
282
  ```yaml
168
- authorization:
169
- default:
170
- header: Authorization
171
- value: Bearer <%= ENV['API_TOKEN'] %> # ERB is supported!
283
+ # Override at spec level
284
+ get_user:
285
+ base_url: https://staging.example.com
286
+ headers:
287
+ x_custom_header: "overridden" # Underscore keys automatically convert to "X-Custom-Header"
288
+
289
+ expectations:
290
+ # Override for a specific expectation
291
+ - base_url: https://prod.example.com
292
+ headers:
293
+ X-Custom-Header: "expectation-specific" # HTTP-style headers used as-is
294
+ expect:
295
+ status: 200
172
296
  ```
173
297
 
174
- ## The Spec Structure
298
+ ## Writing Tests
175
299
 
176
300
  ### Basic Structure
177
301
 
178
- Every spec needs a path, HTTP method, and at least one expectation to be useful:
302
+ Every spec needs a path, HTTP method, and at least one expectation:
179
303
 
180
304
  ```yaml
181
305
  show_user:
182
306
  path: /users/1
183
- method: GET # Optional for GET requests, can be lowercase too if that's your style
307
+ method: GET # Optional for GET requests
184
308
  expectations:
185
- - name: "Retrieves a User" # Recommended. May be required in future versions for OpenAPI generation
186
- expect:
309
+ - expect:
187
310
  status: 200
188
311
  ```
189
312
 
190
313
  ### Testing Response Data
191
314
 
192
- Let's verify the response JSON:
315
+ Verify the response JSON:
193
316
 
194
317
  ```yaml
195
318
  show_user:
196
319
  path: /users/1
197
- method: GET
198
320
  expectations:
199
- - name: "Retrieves a User"
200
- expect:
321
+ - expect:
201
322
  status: 200
202
323
  json:
203
324
  id: 1
@@ -207,58 +328,62 @@ show_user:
207
328
 
208
329
  ### Multiple Expectations
209
330
 
210
- Each expectation can override any spec-level setting. This is useful for testing different scenarios:
331
+ Each expectation can override any spec-level setting:
211
332
 
212
333
  ```yaml
213
334
  show_user:
214
335
  path: /users/1
215
- method: GET
216
336
  expectations:
217
- - name: "Retrieves a User"
218
- expect:
337
+ - expect:
219
338
  status: 200
220
339
  json:
221
340
  id: 1
222
341
  role: admin
223
- - name: "Invalid User ID"
224
- path: /users/999 # Overrides spec-level path
342
+ - path: /users/999 # Overrides spec-level path
225
343
  expect:
226
344
  status: 404
227
345
  ```
228
346
 
229
347
  ### Request Data
230
348
 
231
- For POST/PUT/PATCH requests, you can include query parameters and body data:
349
+ Add query parameters and body data:
232
350
 
233
351
  ```yaml
234
352
  create_user:
235
353
  path: /users
236
354
  method: POST
237
- query: # or 'params' if you prefer
355
+ query: # or "params" if you prefer
238
356
  team_id: 123
239
- body: # or 'data' if you prefer
357
+ body: # or "data" if you prefer
240
358
  name: John Doe
241
359
  email: john@example.com
242
- role: admin
243
360
  expectations:
244
361
  - expect:
245
362
  status: 201
246
- json:
247
- id: kind_of.integer
248
- name: John Doe
249
363
  ```
250
364
 
251
- ## Dynamic Features
365
+ ### Path Parameters
252
366
 
253
- SpecForge provides powerful features for generating and manipulating test data dynamically.
367
+ Use placeholders for dynamic path parameters:
368
+
369
+ ```yaml
370
+ show_user:
371
+ path: /users/{id} # Use {id} or :id
372
+ query:
373
+ id: 1 # Replaces the placeholder
374
+ expectations:
375
+ - expect:
376
+ status: 200
377
+ ```
378
+
379
+ ## Dynamic Features
254
380
 
255
381
  ### Variables
256
382
 
257
- Variables let you define and reuse values across your tests. They support complex chaining and can reference generated data:
383
+ Variables let you define and reuse values:
258
384
 
259
385
  ```yaml
260
386
  list_posts:
261
- path: /posts
262
387
  variables:
263
388
  author: factories.user
264
389
  category_name: faker.lorem.word
@@ -266,24 +391,15 @@ list_posts:
266
391
  author_id: variables.author.id
267
392
  category: variables.category_name
268
393
  expectations:
269
- - name: "Lists user's posts"
270
- expect:
394
+ - expect:
271
395
  status: 200
272
- json:
273
- posts:
274
- matcher.include:
275
- - author:
276
- id: variables.author.id
277
- name: variables.author.name
278
- category: variables.category_name
279
- ```
280
-
281
- Variables support deep traversal:
282
- ```yaml
283
- variables:
284
- user: factories.user
285
- first_post: variables.user.posts.last
286
- author: variables.first_post.comments.2.author.name
396
+ json:
397
+ posts:
398
+ matcher.include:
399
+ - author:
400
+ id: variables.author.id
401
+ name: variables.author.name
402
+ category: variables.category_name
287
403
  ```
288
404
 
289
405
  ### Transformations
@@ -305,47 +421,118 @@ create_user:
305
421
  email: faker.internet.email
306
422
  ```
307
423
 
308
- ### Factory Integration
309
-
310
- SpecForge provides a YAML interface to FactoryBot, making it easy to define and use factories in your tests:
311
-
312
- 1. **Existing FactoryBot Factories**: Out of the box, SpecForge automatically discovers your existing Ruby-defined factories from:
313
- - Standard paths (`spec/factories` and `test/factories`)
314
- - Any custom paths you configure via your `config.yml`:
315
- ```yaml
316
- factories:
317
- # Override default FactoryBot factory paths
318
- paths:
319
- - custom/factories/path
320
-
321
- # Disable automatic factory discovery if needed (default: true)
322
- auto_discover: false
323
- ```
324
-
325
- 2. **YAML Factory Definitions**: Define factories using YAML in `spec_forge/factories/`:
326
- ```yaml
327
- # spec_forge/factories/user.yml
328
- user:
329
- class: User # Optional model class name
330
- attributes:
331
- name: faker.name.name
332
- email: faker.internet.email
333
- role: admin
334
- ```
335
- SpecForge registers these YAML definitions with FactoryBot, making them work exactly like Ruby-defined factories.
336
-
337
- Use factories in your tests:
424
+ ### Chaining
425
+
426
+ Access nested attributes and methods through chaining:
427
+
428
+ ```yaml
429
+ list_posts:
430
+ variables:
431
+ # Factory chaining examples
432
+ owner: factories.user # Creates a user
433
+ name: factories.user.name # Gets just the name
434
+ company: variables.owner.company # Access factory attributes
435
+
436
+ # Variable chaining for relationships
437
+ first_post: variables.user.posts.first
438
+
439
+ # You can use array indices directly
440
+ comment_author: variables.first_post.comments.2.author.name
441
+
442
+ # Faker method chaining
443
+ lowercase_email: faker.internet.email.downcase
444
+ title_name: faker.name.first_name.titleize
445
+ ```
446
+
447
+ ### Factory Build Strategies
448
+
449
+ Control how factories create objects and customize their attributes:
450
+
451
+ ```yaml
452
+ create_user:
453
+ variables:
454
+ # Default strategy (create)
455
+ regular_user: factories.user
456
+
457
+ # Custom build strategy and attributes
458
+ custom_user:
459
+ factory.user:
460
+ strategy: build # 'create' (default) or 'build'
461
+ attributes:
462
+ name: "Custom Name"
463
+ email: faker.internet.email
464
+ ```
465
+
466
+ ## Factory Support
467
+
468
+ ### Automatic Discovery
469
+
470
+ SpecForge automatically discovers factories in standard paths:
471
+
472
+ ```ruby
473
+ SpecForge.configure do |config|
474
+ # Disable automatic factory discovery if needed (default: true)
475
+ config.factories.auto_discover = false
476
+ end
477
+ ```
478
+
479
+ ### Custom Factory Paths
480
+
481
+ Add custom paths to the factory search list:
482
+
483
+ ```ruby
484
+ SpecForge.configure do |config|
485
+ # Add custom factory paths (appends to default paths)
486
+ # Ignored if `auto_discovery` is false
487
+ config.factories.paths += ["lib/factories"]
488
+ end
489
+ ```
490
+
491
+ ### Factory Build Strategies
492
+
493
+ Control how factories create objects and customize their attributes:
494
+
338
495
  ```yaml
339
- create_post:
496
+ create_user:
340
497
  variables:
341
- author: factories.user # Works with both YAML and Ruby-defined factories
498
+ # Default strategy (create)
499
+ regular_user: factories.user
500
+
501
+ # Custom build strategy and attributes
502
+ custom_user:
503
+ factory.user:
504
+ strategy: build # 'create' (default) or 'build'
505
+ attributes:
506
+ name: "Custom Name"
507
+ email: faker.internet.email
508
+ ```
509
+
510
+ ### YAML Factory Definitions
511
+
512
+ Define factories in YAML with a simple declarative syntax:
513
+
514
+ ```yaml
515
+ # spec_forge/factories/user.yml
516
+ user:
517
+ class: User # Optional model class name
518
+ variables:
519
+ department: faker.company.department
520
+ team_size:
521
+ faker.number.between:
522
+ from: 5
523
+ to: 20
524
+ attributes:
525
+ name: faker.name.name
526
+ email: faker.internet.email
527
+ role: admin
528
+ department: variables.department
529
+ team_count: variables.team_size
342
530
  ```
343
531
 
344
532
  ## RSpec Matchers
345
533
 
346
- SpecForge provides access to RSpec's powerful matcher system through an intuitive dot notation syntax. The matcher system dynamically integrates with RSpec's matchers through three main namespaces:
534
+ ### "be" namespace
347
535
 
348
- #### "be" namespace
349
536
  ```yaml
350
537
  expect:
351
538
  json:
@@ -356,22 +543,23 @@ expect:
356
543
  tags: be.empty
357
544
  email: be.present
358
545
 
359
- # Comparisons (aliases available)
546
+ # Comparisons
360
547
  price:
361
- be.greater_than: 18 # be.greater also works
548
+ be.greater_than: 18
362
549
  stock:
363
- be.less_than_or_equal: 100 # be.less_or_equal also works
550
+ be.less_than_or_equal: 100
364
551
  rating:
365
552
  be.between:
366
553
  - 1
367
554
  - 5
368
555
 
369
556
  # Dynamic predicate methods
370
- published: be.published # Maps to be_published
371
- admin: be.admin # Maps to be_admin
557
+ published: be.published
558
+ admin: be.admin
372
559
  ```
373
560
 
374
- #### "kind_of" namespace
561
+ ### "kind_of" namespace
562
+
375
563
  ```yaml
376
564
  expect:
377
565
  json:
@@ -381,11 +569,11 @@ expect:
381
569
  scores: kind_of.array
382
570
  ```
383
571
 
384
- #### "matchers" namespace
572
+ ### "matchers" namespace
573
+
385
574
  ```yaml
386
575
  expect:
387
576
  json:
388
- # Direct RSpec matcher usage
389
577
  tags:
390
578
  matcher.include:
391
579
  - featured
@@ -393,19 +581,54 @@ expect:
393
581
 
394
582
  slug: /^[a-z0-9-]+$/ # Shorthand for matching regexes
395
583
 
396
- # Any RSpec matcher can be used
397
584
  config:
398
585
  matcher.have_key: api_version
399
586
  ```
400
587
 
401
- Note: Matchers that require Ruby blocks (like `change`) are not supported.
588
+ ## How Tests Work
402
589
 
403
- ## Roadmap
590
+ When you write a YAML spec, SpecForge converts it into an RSpec test structure. For example, this YAML:
404
591
 
405
- - [ ] Negated matchers
406
- - [ ] OpenAPI generation from your tests
407
- - [ ] Support for XML/HTML response handling
408
- - [ ] Support for running individual specs
592
+ ```yaml
593
+ create_user:
594
+ path: /users
595
+ method: POST
596
+ variables:
597
+ full_name: faker.name.name
598
+ body:
599
+ name: variables.full_name
600
+ expectations:
601
+ - expect:
602
+ status: 201
603
+ json:
604
+ name: variables.full_name
605
+ ```
606
+
607
+ Becomes this RSpec test:
608
+
609
+ ```ruby
610
+ RSpec.describe "create_user" do
611
+ describe "POST /users" do
612
+ let(:full_name) { Faker::Name.name }
613
+
614
+ let!(:expected_status) { 201 }
615
+ let!(:expected_json) do
616
+ {
617
+ name: eq(full_name)
618
+ }
619
+ end
620
+
621
+ subject(:response) do
622
+ post("/users", body: { name: full_name })
623
+ end
624
+
625
+ it do
626
+ expect(response.status).to eq(expected_status)
627
+ expect(response.body).to include(expected_json)
628
+ end
629
+ end
630
+ end
631
+ ```
409
632
 
410
633
  ## Contributing
411
634