brine-dsl 0.5.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 (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +3 -0
  6. data/Gemfile.lock +123 -0
  7. data/Guardfile +12 -0
  8. data/LICENSE +21 -0
  9. data/README.md +137 -0
  10. data/Rakefile +32 -0
  11. data/brine-dsl.gemspec +32 -0
  12. data/config/cucumber.yml +2 -0
  13. data/docs/build.gradle +19 -0
  14. data/docs/cookbook.html +567 -0
  15. data/docs/gradle/wrapper/gradle-wrapper.jar +0 -0
  16. data/docs/gradle/wrapper/gradle-wrapper.properties +6 -0
  17. data/docs/gradlew +172 -0
  18. data/docs/gradlew.bat +84 -0
  19. data/docs/guide.html +1149 -0
  20. data/docs/index.html +472 -0
  21. data/docs/specs.html +1672 -0
  22. data/docs/src/cookbook.adoc +87 -0
  23. data/docs/src/guide.adoc +427 -0
  24. data/docs/src/index.adoc +16 -0
  25. data/docs/src/spec.erb +121 -0
  26. data/docs/src/specs.adoc +24 -0
  27. data/features/argument_transforms/boolean.feature +37 -0
  28. data/features/argument_transforms/datetime.feature +45 -0
  29. data/features/argument_transforms/integer.feature +41 -0
  30. data/features/argument_transforms/list.feature +46 -0
  31. data/features/argument_transforms/object.feature +66 -0
  32. data/features/argument_transforms/quoted.feature +41 -0
  33. data/features/argument_transforms/regex.feature +40 -0
  34. data/features/argument_transforms/template.feature +46 -0
  35. data/features/argument_transforms/whitespace.feature +51 -0
  36. data/features/assertions/is_a_valid.feature +184 -0
  37. data/features/assertions/is_equal_to.feature +60 -0
  38. data/features/assertions/is_including.feature +29 -0
  39. data/features/assertions/is_matching.feature +35 -0
  40. data/features/deprecations/replaced_with.feature +35 -0
  41. data/features/request_construction/basic.feature +29 -0
  42. data/features/request_construction/body.feature +26 -0
  43. data/features/request_construction/clearing.feature +46 -0
  44. data/features/request_construction/headers.feature +94 -0
  45. data/features/request_construction/params.feature +60 -0
  46. data/features/resource_cleanup/cleanup.feature +86 -0
  47. data/features/selectors/all.feature +55 -0
  48. data/features/selectors/any.feature +48 -0
  49. data/features/step_definitions/test_steps.rb +5 -0
  50. data/features/support/env.rb +10 -0
  51. data/lib/brine/cleaner_upper.rb +62 -0
  52. data/lib/brine/coercer.rb +18 -0
  53. data/lib/brine/hooks.rb +4 -0
  54. data/lib/brine/mustache_binder.rb +25 -0
  55. data/lib/brine/requester.rb +125 -0
  56. data/lib/brine/rest_steps.rb +138 -0
  57. data/lib/brine/selector.rb +66 -0
  58. data/lib/brine/step_definitions/assertions.rb +37 -0
  59. data/lib/brine/step_definitions/assignment.rb +13 -0
  60. data/lib/brine/step_definitions/cleanup.rb +4 -0
  61. data/lib/brine/step_definitions/request_construction.rb +19 -0
  62. data/lib/brine/step_definitions/selection.rb +37 -0
  63. data/lib/brine/test_steps.rb +138 -0
  64. data/lib/brine/transforms.rb +81 -0
  65. data/lib/brine/type_checks.rb +35 -0
  66. data/lib/brine/util.rb +35 -0
  67. data/lib/brine.rb +39 -0
  68. data/tutorial/missing.feature +5 -0
  69. data/tutorial/post_matching.feature +12 -0
  70. data/tutorial/post_status.feature +10 -0
  71. data/tutorial/support/env.rb +2 -0
  72. metadata +306 -0
@@ -0,0 +1,87 @@
1
+ = icon:cutlery[] Brine Cookbook
2
+ Matt Whipple <http://github.com/mwhipple[@mwhipple]>
3
+ :description: Cookbook for the Brine REST Testing DSL
4
+ :keywords: Brine, Cucumber, REST, DSL
5
+
6
+ == Overview
7
+ The following are some recipes to address issues which may arise while using
8
+ Brine but are not considered part of Brine's responsibility (at least presently).
9
+ Many of these are focused on simplicity and ease rather than robustness or elegance,
10
+ and modification to address specific cases should be expected.
11
+
12
+ == "Assurances"
13
+ === Context
14
+ Ideally all tests should be as self-contained and isolated as possible;
15
+ when writing functional tests, however, there are cases where this isn't
16
+ feasible or possible. In some cases a system depends on another external
17
+ system which is not a system that is under test and which (for whatever reason)
18
+ cannot be easily worked with. In white box testing such a system would likely be
19
+ represented by some form of test double, but this may be unfeasible and/or
20
+ undesirable when testing a deployed system.
21
+
22
+ An example of such a system is user/account management which often incurs
23
+ additional overhead to provision a new account. When testing a secured
24
+ system valid accounts are needed for representative testing, but provisioning
25
+ a new account may be difficult or outside the scope of the system that is being
26
+ actively tested. If tested functionality involves enacting account-wide changes
27
+ and the number of accounts is limited, then that is likely to unfortunately prevent
28
+ complete test isolation.
29
+
30
+ === Solution
31
+ In such cases a standard solution is to designate certain resources to be reused for
32
+ certain tests. These are analogous to the concept of "fixtures" in some test suites
33
+ though there may be slight differences in implementation and reliance on them.
34
+ Here the term "assurances" is used..primarily because it starts with `a` which lends
35
+ itself to relevant files being listed towards the beginning alphabetically in a given directory.
36
+
37
+ The goal of assurances is to specify conditions which are expected before other
38
+ tests are to be run. Preferably the dependent tests should also explicitly declare the
39
+ dependency but a significant solution for that is not established. Assurances therefore
40
+ validate that preconditions are met; ideally if such preconditions can be established
41
+ idempotently then the assurances can do so before the validation.
42
+
43
+ ==== Assurances are NOT Tests
44
+ **Assurances validate a state which is desired to be consistently retained within the
45
+ system rather than being changed**. This means that they should _not_ be used for tests
46
+ as that would require state changes, nor should they clean up after themselves (as that
47
+ would also imply a state change). If assurances are configured for a system which should
48
+ also be tested, then appropriate tests should exist (including those that may validate any
49
+ behavior relied upon by the assurance).
50
+
51
+ ==== Consequences
52
+ As mentioned previously assertions help in cases where tests cannot be fully isolated,
53
+ and therefore some known state must be established and reused across tests (and such state
54
+ should *not* change). A practical reason for this is to allow for overlapping test executions.
55
+ If tests are not fully isolated, state is being changed, and test runs overlap, then tests
56
+ may fail non-deterministically due to one test run pulling the state out from another. This
57
+ in the simplest form can be a nuisance but it also effectively precludes the ability to speed
58
+ up test runs through the use of parallelism/asynchronicity.
59
+
60
+ _TODO: Enumerate drawbacks_
61
+
62
+ === Recipe
63
+ This can be done using standard cucumber tags. Assurances can be defined in designated
64
+ `assure_*.feature` files where each Feature is appropriately tagged:
65
+ [source,gherkin]
66
+ ----
67
+ @assure
68
+ Feature: Some preconditions are verified...
69
+ ----
70
+ And then a Rake task is added to run those tagged features:
71
+ [source,ruby]
72
+ ----
73
+ Cucumber::Rake::Task.new(:assure) do |t|
74
+ t.cucumber_opts = "#{ENV['CUCUMBER_OPTS']} --tags @assure"
75
+ end
76
+ ----
77
+ The Rake task that runs the other tests then depends on that task:
78
+ [source,ruby]
79
+ ----
80
+ task :invoke_cuke => [:assure] do
81
+ #Run cucumber, potentially in parallel and likely with --tags `@assure`
82
+ end
83
+ ----
84
+ This approach tests preconditions and will avoid running the rest of the tests if they
85
+ are not (relying on standard Rake behavior). The assurances can also be run with different
86
+ Cucumber behavior so that the full test suite can be more stochastic
87
+ (randomized/non-serialized) while the assurances can be more controlled.
@@ -0,0 +1,427 @@
1
+ = icon:book[] Brine User Guide
2
+ Matt Whipple <http://github.com/mwhipple[@mwhipple]>
3
+ :description: The User Guide for using the Brine REST Testing DSL
4
+ :keywords: Brine, Cucumber, REST, DSL
5
+ :grave: `
6
+
7
+ Cucumber DSL for testing REST APIs
8
+
9
+ == Introduction
10
+
11
+ === Motivation
12
+ REpresentational State Transfer APIs expose their functionality
13
+ through combinations of fairly coarse primitives that generally
14
+ revolve around the use of transferring data in a standard exchange
15
+ format (such as JSON) using HTTP methods and other aspects of the very
16
+ simple HTTP protocol. Tests for such an API can therefore be defined
17
+ using a domain specific language (DSL) built around those higher level
18
+ ideas rather than requiring a general purpose language (the equivalent
19
+ of scripted `curl` commands with some glue code and assertions).
20
+ This project provides such a DSL by using select libraries
21
+ integrated into Cucumber, where Cucumber provides a test-oriented
22
+ framework for DSL creation.
23
+
24
+ === Sample Usage
25
+ The general usage pattern revolves around construction of a request
26
+ and performing assertions against the received response.
27
+
28
+ [source,gherkin]
29
+ ----
30
+ When the request body is assigned:
31
+ """
32
+ {"first_name": "John",
33
+ "last_name": "Smith"}
34
+ """
35
+ And a POST is sent to `/users`
36
+ Then the value of the response status is equal to `200`
37
+ And the value of the response body is including:
38
+ """
39
+ {"first_name": "John",
40
+ "last_name": "Smith"}
41
+ """
42
+ ----
43
+
44
+ === Key Features
45
+ Variable Binding/Expansion::
46
+ In cases where dynamic data is in the response or is desired for the
47
+ request, then values can be bound to identifiers which can then be
48
+ expanded using http://mustache.github.io[Mustache] templates in your
49
+ feature files.
50
+
51
+ Type Transforms::
52
+ Different types of data can be expressed directly in the feature files
53
+ or expanded into variables by using the appropriate syntax for that
54
+ type.
55
+
56
+ Type Coercion::
57
+ Related to transforms, a facility to coerce types is also provided. This allows
58
+ more intelligent comparison of inputs which have been transformed to a
59
+ richer data type with those that have not been transformed (normally strings).
60
+ As an example comparing a date/time value with a string will attempt to parse
61
+ the string to a date/time so that the values can be compared using the proper semantics.
62
+
63
+ <<_resource_cleanup>>::
64
+ Tests are likely to create resources which should then be cleaned up,
65
+ restoring the pre-test state of the system: steps to facilitate this
66
+ are provided.
67
+
68
+ Authentication::
69
+ Presently OAuth2 is supported to issue authenticated requests during a
70
+ test (likely using a feature `Background`).
71
+
72
+ Request Construction and Response Assertion Step Definitions::
73
+ The previous features combined with the library of provide steps should
74
+ cover all of the functionality needed to exercise and validate all of
75
+ the functionality exposed by your REST API.
76
+
77
+ == Installation
78
+ Presently the gem for this project isn't being published anywhere:
79
+ primarily because tracking down a local gem repository seems scary. If
80
+ this project is open sourced then this will probably change but in the
81
+ meantime the project can be used off of GitHub by adding this to your
82
+ `Gemfile` and performing the usual `bundle install` dance:
83
+
84
+ [source,ruby]
85
+ ----
86
+ git 'git@github.com:brightcove/brine.git', :branch => 'master' do
87
+ gem 'brine'
88
+ end
89
+ ----
90
+
91
+ Specific branches and refs can be targetted as
92
+ documented http://bundler.io/git.html[here]. This should likely be
93
+ done in any cases where you're not actively tracking Brine and don't want
94
+ your tests to suddenly break because of changes to it.
95
+
96
+ Brine can then be "mixed in" to your project (which adds assorted
97
+ modules to the `World` and loads all the step definitions and other
98
+ Cucumber magic) by adding the following to your `support/env.rb` or
99
+ other ruby file:
100
+
101
+ [source,ruby]
102
+ ----
103
+ require 'brine'
104
+
105
+ World(brine_mix)
106
+ ----
107
+
108
+ Select pieces can also be loaded (to be documented). With the above,
109
+ feature files should be able to be written and executed without
110
+ requiring any additional ruby code.
111
+
112
+ == Tutorial
113
+ We'll write some tests against http://myjson.com/api
114
+ (selected fairly arbitrary from the list at https://github.com/toddmotto/public-apis).
115
+ The API is being explored for the sake of this tutorial,
116
+ which also serves to bolster this library to support the effort.
117
+
118
+ === Selecting a ROOT_URL
119
+ Brine expects steps to use relative URLs. The feature files specify
120
+ the behavior of an API (or multiple APIs), while the root of the
121
+ URLs define where that API is, so this is a natural mapping.
122
+ More practically, when developing an API it's likely to
123
+ be promoted across various environments such as
124
+ local, qa, stage, and production so having a parameterized root for
125
+ the URLs eases this while encouraging inter-environment consistency.
126
+
127
+ For simple cases where all tests are to be run against the same root,
128
+ the root url can be specified with the environment variable `ROOT_URL`,
129
+ such as `ROOT_URL=https://api.myjson.com/ cucumber`, or letting `rake`
130
+ take care of this for you such as:
131
+ [source,ruby]
132
+ ----
133
+ Cucumber::Rake::Task.new do
134
+ ENV['ROOT_URL'] = 'https://api.myjson.com/'
135
+ end
136
+ ----
137
+ which could then be called with `rake cucumber`. The rake approach
138
+ can be extended for different tasks for each environment, each
139
+ of which sets the appropriate environment variables allowing the
140
+ test code itself to follow https://12factor.net/config[Twelve-Factor App guidelines]
141
+ where Rake provides sugary convenience.
142
+
143
+ === A Basic GET
144
+ Most tests will involve some form of issuing requests and performing assertions
145
+ on the responses. Let's start with a simple version of that pattern,
146
+ testing the response status from a GET request.
147
+
148
+ [source,gherkin]
149
+ ----
150
+ include::../../tutorial/missing.feature[]
151
+ ----
152
+
153
+ === A Write Request
154
+ For POST, PATCH and PUT requests you'll normally want to include a request body.
155
+ To support this, additional data can be added to the requests before they are sent
156
+ (see <<_request_construction>>).
157
+
158
+ [source,gherkin]
159
+ ----
160
+ include::../../tutorial/post_status.feature[]
161
+ ----
162
+
163
+ === Test Response Properties
164
+ The API that was chosen for testing returns the link to the created resource
165
+ which is based off of a generated id. That means that the exact response cannot
166
+ be verified, but instead property based testing can be done to verify that the
167
+ data is sane and therefore likely trustworthy. In this case we
168
+ can check that the `uri` response child matches the expected pattern.
169
+
170
+ [source,gherkin]
171
+ ----
172
+ include::../../tutorial/post_matching.feature[]
173
+ ----
174
+
175
+ ////
176
+ === Known Response Data
177
+ One of the simplest and most obvious things to test for is that the response
178
+ contains data for which exact values are expected. Continuing from above we
179
+ can check that the response body returns the fields that we provided.
180
+
181
+ [source,gherkin]
182
+ ----
183
+ include::../../tutorial/post_including.feature[]
184
+ ----
185
+ ////
186
+
187
+ == Environment Variables
188
+ Some Brine behavior can be tuned by passing it appropriate environment variables, listed here.
189
+
190
+ `BRINE_LOG_HTTP`::
191
+ Output HTTP traffic to stdout. Any truthy value will result in request and response
192
+ metadata being logged, a value of `DEBUG` (case insensitive) will also log the bodies.
193
+
194
+ `BRINE_LOG_BINDING`::
195
+ Log values as they are assigned to variables in Brine steps.
196
+
197
+ == Language Conventions
198
+ === The use of ``{grave}``s
199
+ Backticks/grave accents are used as _parameter delimiters_. It is perhaps
200
+ most helpful to think of them in those explicit terms rather than thinking of them
201
+ as an alternate _quote_ construct. In particular quoting implies that the parameter
202
+ value is a string value, while the step transforms allow for alternate data types.
203
+
204
+ ``{grave}``s were chosen as they are less common than
205
+ many other syntactical elements and also allow for the use of logically significant
206
+ quoting within paremeter values while hopefully avoiding the need for escape artistry
207
+ (as used for argument transforms).
208
+
209
+ == Selection and Assertion
210
+ As tests are generally concerned with performing assertions, a testing DSL should be
211
+ able to express the variety of assertions that may be needed. Because these are likely
212
+ to be numerous, it could easily lead to duplicated logic or geometric growth of code due
213
+ to the combinations of types of assertions and the means to select the inputs for the assertion.
214
+
215
+ To avoid this issue the concepts of selection and assertion are considered separate operations in Brine.
216
+ Internally this corresponds to two steps: the first assigns a selector;
217
+ the second passes the assertion to that selector which is responsible for applying the assertion against
218
+ the selected value(s). In standard step use this will still be expressed as a single step,
219
+ and dynamic step definitions are used to split the work appropriately.
220
+
221
+ For example the step:
222
+ [source,gherkin]
223
+ ----
224
+ Then the value of the response body is equal to `foo`
225
+ ----
226
+ Will be split where the subject of the step (`the value of the response body`)
227
+ defines the selector and the predicate of the step `is equal to {grave}foo{grave}` defines
228
+ the assertion (which is translated to a step such as `Then it is equal to {grave}foo{grave}`).
229
+
230
+ The result of this is that the assertion steps will always follow a pattern where the subject
231
+ resembles `the value of ...` and the predicate always resembles `is ...`. Learning the selection
232
+ phrases and the assertion phrases and combining them should be a more efficient and flexible way
233
+ to become familiar with the language instead of focusing on the resulting combined steps.
234
+
235
+ The chosen approach sacrifices eloquence for the sake of consistency.
236
+ The predicate will always start with `is` which can lead to awkward language such as
237
+ `is including` rather than simply `includes`.
238
+ The consistency provides additional benefits such as consistent modification:
239
+ for instance `is not` can always be use for negation rather than working out the appropriate
240
+ phrasing for a more natural sounding step (let alone the logic).
241
+
242
+ One of the secondary goals of this is that assertion step definitions should very simple to
243
+ write and modifiers (such as negation) should be provided for free to those definitions.
244
+ As assertion definitions are likely to be numerous and potentially customized, this should help optimize code economy.
245
+
246
+ === Selection Modifiers
247
+ To pursue economical flexibility Brine steps attempt to balance step definitions which accommodate variations
248
+ while keeping the step logic and patterns fairly simple. Selection steps in particular generally accept some
249
+ parameters that affect their behavior. This allows the relatively small number of selection steps to provide
250
+ the flexibility to empower the more numerous assertion steps.
251
+
252
+ ==== Traversal
253
+ Selection steps can generally target the root of the object specified (such as the response body)
254
+ or some nodes within the object if it is a non-scalar value (for instance a child of the response body).
255
+ This is indicated in the <<_selection,step reference selection steps>> by the `[$TRAVERSAL]` placeholder.
256
+ `child {grave}$EXPRESSION{grave}` or `children {grave}$EXPRESSION{grave}` can optionally be
257
+ inserted at the placeholder to select nested nodes as described in <<_traversal_2>>.
258
+
259
+ ==== Negation
260
+ The selectors also currently handle negation of the associated assertions.
261
+ This is potentially counter-intuitive but as previously mentioned the intent is that this
262
+ should ease the creation of assertions. If negation is added to a selector that it is expected that
263
+ the assertion will _fail_.
264
+
265
+ Negation will be normally indicated in the <<_selection,step reference selection steps>> by the presence
266
+ of the `[not]` placeholder. A similar placeholder may be used that is more readable but leads to an equivalent
267
+ inversion of the semantics of the statement. To negate the step, the text within the ``[]``s should be inserted
268
+ in the indicated position.
269
+
270
+ [NOTE, caption='Future Versions']
271
+ Handling this in the selectors is (as mentioned) counter-intuitive and unnecessarily couples the selector
272
+ to the assertion. It is currently done for practical reasons but is likely to be replaced in a future version
273
+ after (or as part of) the initial port of the library to another platform. When it is replaced, all existing steps
274
+ will remain supported through at least one more major revision and most should (most should remain unchanged).
275
+
276
+ === Chained Assertions
277
+ [WARNING, caption='Unsupported Feature']
278
+ Use at your own risk, this feature is *not presently supported*.
279
+
280
+ For anyone that likes to live on the (relative) edge or if this gathers notable interest...the above also
281
+ provides an implicit feature: after a value is selected multiple assertions could be performed against it.
282
+ For instance:
283
+
284
+ [source,gherkin]
285
+ ----
286
+ Then the value of the response body is equal to `foo`
287
+ And it is of the type `String`
288
+ ----
289
+ Though this may work in simple cases the present design is likely to produce surprising results since
290
+ some aspects (such as negation) are handled by the selector so it would be inherited by the conjunctions
291
+ even though it wouldn't read that way.
292
+
293
+ == Traversal
294
+ The language exposed by Brine is flat but the data returned by the server is likely
295
+ to include deeper data structures such as objects and collections. To allow selection within
296
+ such structures a `traversal` language is embedded within some steps which will be indicated
297
+ by the use of the `TRAVERSAL` placeholder.
298
+
299
+ The traversal language consists of a selected subset of http://goessner.net/articles/JsonPath/[JsonPath].
300
+
301
+ [NOTE, caption='The Selected Subset']
302
+ The subset of JsonPath functionality has been chosen that is believed to support all needed
303
+ test cases without requiring deep familiarity with JsonPath. This may lead to more numerous simple steps
304
+ in place of fewer steps that use unsupported expressions. Additionally Brine is intended to be
305
+ ported to a range of platforms and so only those steps outlined here will be supported across those platforms.
306
+ JsonPath expressions _not_ listed below will not be explicitly disallowed but are not officially supported
307
+ (will not be tested and will not be ported to another platform if needed).
308
+
309
+ === Cardinality
310
+ Each traversal expression will select _all_ matching nodes which is therefore represented as a collection.
311
+ Often, however, only a single node is expected or desired. Therefore the traversal expression will also
312
+ be accompanied by a phrase which defines the expected cardinality, normally `child` vs. `children`. `children` will
313
+ _always_ return an array while `child` will return what would be the first element in that array. `child` should be
314
+ used when accessing a specific node within the tree, while `children` should be used for what amounts to a query
315
+ across multiple nodes (such as testing the value of a field for every element in a collection).
316
+
317
+ === Expressions
318
+ `.$KEY`::
319
+ Access the `KEY` named child of the starting node. The leading `.` can be
320
+ omitted if at the start of an expression.
321
+
322
+ == Resource Cleanup
323
+ All test suites should clean up after themselves as a matter of hygiene and to help enforce test independence
324
+ and reproducibility. This is particularly important for this library given that it is likely the systems under test
325
+ are likely to remain running; accumulated uncleaned resources are at best a nuisance to have to weed through and
326
+ at worst raise some costs or other due to heightened consumption of assorted resources (as opposed to more
327
+ ephemeral test environments).
328
+
329
+ Brine therefore provides mechanisms to assist in cleaning up those resources which are created as part of a test run.
330
+ A conceptual hurdle for this type of functionality is that it is very unlikely to be part of the feature that is being
331
+ specified, and therefore should ideally not be part of the specification. Depending on the functionality
332
+ (and arguably the https://www.martinfowler.com/articles/richardsonMaturityModel.html[maturity]) of the
333
+ API, most or all of the cleanup can be automagically done based on convention. There are tentative plans to support
334
+ multiple techniques for cleaning up resources based on how much can be implicitly ascertained...though presently there
335
+ exists only one.
336
+
337
+ === Step indicating resource to DELETE
338
+ If the API supports DELETE requests to remove created resources but it is either desirable or necessary to specify
339
+ what those resource PATHS are, a step can be used to indicate which resources should be DELETEd upon test completion.
340
+
341
+ _see <<_cleanup,Cleanup Step Definitions>>_
342
+
343
+ == Step Reference
344
+ === Request Construction
345
+ link:specs.html#_request_construction[icon:cogs[] Specification]
346
+
347
+ The requests which are sent as part of a test are constructed using
348
+ a https://en.wikipedia.org/wiki/Builder_pattern[Builder].
349
+
350
+ `When a $METHOD is sent to {grave}$PATH{grave}`::
351
+ As every request to a REST API is likely to have a significant
352
+ HTTP `METHOD` and `PATH`, this step is considered required and is therefore used
353
+ to send the built request. This should therefore be the *last* step for any
354
+ given request that is being built.
355
+
356
+ `When the request body is assigned:`::
357
+ The multiline content provided will be assigned to the body of the request.
358
+ This will normally likely be the JSON representation of data.
359
+
360
+ `When the request query parameter {grave}$PARAMETER{grave} is assigned {grave}$VALUE{grave}`::
361
+ Assign `VALUE` to the request query `PARAMETER`.
362
+ The value will be URL encoded and the key/value pair appended to the URL using
363
+ the appropriate `?` or `&` delimiter.
364
+ The order of the parameters in the resulting URL should be considered undefined.
365
+
366
+ `When the request header {grave}$HEADER{grave} is assigned {grave}$VALUE{grave}`::
367
+ Assign `VALUE` to the request header `HEADER`.
368
+ Will overwrite any earlier value for the specified header, including earlier steps or defaults.
369
+
370
+ === Cleanup
371
+ `When a resouce is created at {grave}$PATH{grave}`::
372
+ Mark `PATH` as a resource to DELETE after the test is run. See <<_resource_cleanup>>
373
+
374
+ === Assignment
375
+ `When {grave}$IDENTIFIER{grave} is assigned {grave}$VALUE{grave}`::
376
+ Assigns `VALUE` to `IDENTIFIER`.
377
+
378
+ `When {grave}$IDENTIFIER{grave} is assigned a random string`::
379
+ Assigns a random string (UUID) to `IDENTIFIER`.
380
+ This is particularly useful to assist with test isolation.
381
+
382
+ `When {grave}$IDENTIFIER{grave} is assigned a timestamp`::
383
+ Assigns to `IDENTIFIER` a timestamp value representing the instant at
384
+ which the step was evaluated.
385
+
386
+ === Selection
387
+ link:specs.html#_selection[icon:cogs[] Specification]
388
+
389
+ _see <<_selection_and_assertion>>_
390
+
391
+ `Then the value of the response status is`::
392
+ Select the status code of the current HTTP response.
393
+
394
+ `Then the value of the response body [$TRAVERSAL] is [not]`::
395
+ Select the value from the body of the response.
396
+
397
+ `Then the value of the response body [$TRAVERSAL] does have any element that is [not]`::
398
+ Select any (at least one) element from the structure within the response body.
399
+
400
+ `Then the value of the response body [$TRAVERSAL] has elements which are all`::
401
+ Select all elements from the structure within the response body.
402
+
403
+ === Assertion
404
+ link:specs.html#_assertion[icon:cogs[] Specification]
405
+
406
+ _see <<_selection_and_assertion>>_
407
+
408
+ `Then it is equal to {grave}$VALUE{grave}`::
409
+ Assert that the current selected value is equivalent to `VALUE`
410
+
411
+ `Then it is matching {grave}$VALUE{grave}`::
412
+ Assert that the current select value matches the regular expression `VALUE`
413
+
414
+ `Then it is including {grave}$VALUE{grave}`::
415
+ Assert that the current select value includes/is a superset of `VALUE`.
416
+
417
+ `Then it is a valid {grave}$TYPE{grave}`::
418
+ Assert that the selected value is a valid instance of a `TYPE`. Presently this
419
+ is focused on standard data types (intially based on those specified by JSON),
420
+ but it is designed to handle user specified domain types pending some minor
421
+ wiring and documentation. The current supported types are:
422
+ * `Object`: A JSON style object/associative array
423
+ * `String`
424
+ * `Number`
425
+ * `Integer`
426
+ * `Array`
427
+ * `Boolean`
@@ -0,0 +1,16 @@
1
+ = Brine
2
+ Matt Whipple <http://github.com/mwhipple[@mwhipple]>
3
+ :description: A Cucumber-based DSL for testing REST APIs
4
+ :keywords: Brine, Cucumber, REST, DSL
5
+
6
+ Cucumber DSL for testing REST APIs
7
+
8
+ == Documentation
9
+ The following are the documentation resources presently available.
10
+
11
+ link:guide.html[icon:book[] User Guide]::
12
+ A guide to writing specifications using the Brine provided DSL.
13
+ link:specs.html[icon:cogs[] Specification]::
14
+ The Gherkin specification for all of Brine's features.
15
+ link:cookbook.html[icon:cutlery[] Cookbook]::
16
+ Solutions to some problems which Brine does not solve directly.
data/docs/src/spec.erb ADDED
@@ -0,0 +1,121 @@
1
+ <%# TODO: Indentation can't be used because of significance to adoc...comment liberally or abuse tags %>
2
+ <%# TODO: Add a lot of CSS classes so the output can be matched back to this template %>
3
+
4
+ === [feature name]#<%= feature['name'] %>#
5
+
6
+ <%if feature.key?('description') %>
7
+ <%# FIXME: Styling doesn't work here and description seems to contain background %>
8
+ <%= feature['description'] %>
9
+ <% end %>
10
+
11
+ <%if feature.key?('background') %>
12
+ ==== [background name]#<%= feature['background']['name'] %>#
13
+
14
+ <%if feature['background'].key?('description') %>
15
+ [background description]#<%= feature['background']['description'] %>#
16
+ <% end %>
17
+
18
+ <%if feature['background'].key?('steps') %>
19
+ [.step-list]
20
+ <% feature['background']['steps'].each do |step| %>
21
+ * *<%= step['keyword'].strip %>* <%= step['name'] %>
22
+
23
+ <%if step.key?('doc_string') %>
24
+ [source,gherkin]
25
+ ----
26
+ <%= step['doc_string']['value'] %>
27
+ ----
28
+ <% end %>
29
+
30
+ <%if step.key?('rows') %>
31
+ +<% if step['rows'].first && step['rows'].first.key?('cols') %>
32
+ [<%= step['rows'].first['cols'] %>]
33
+ <% end %>
34
+
35
+ |====
36
+ <% step['rows'].each_with_index do |row, index| %>
37
+ <% row['cells'].each_with_index do |cell, i| %>
38
+ <% if row.key?('cell-styles') && row['cell-styles'].length > i %>
39
+ <%= row['cell-styles'][i] %>
40
+ <% end %>
41
+ | <%= cell %>
42
+ <% end %>
43
+ <% end %>|====
44
+ <% end %>
45
+ <% end %>
46
+ <% end %>
47
+
48
+ <%if feature['background'].key?('examples')
49
+ example = feature['background']['examples'] %>
50
+
51
+ ==== EX: <%= example['keyword'].strip %> <%= example['name'] %>
52
+
53
+ <% if example['rows'].first && example['rows'].first.key?('cols') %>
54
+ [<%= example['rows'].first['cols'] %>]
55
+ <% end %>
56
+
57
+ |====
58
+ <% example['rows'].each_with_index do |row, index| %>
59
+ <% row['cells'].each_with_index do |cell, i| %>
60
+ <% if row.key?('cell-styles') && row['cell-styles'].length > i %>
61
+ <%= row['cell-styles'][i] %>
62
+ <% end %>
63
+ | <%= cell %>
64
+ <% end %>
65
+ <% if index == 0 %>
66
+ <% end %>
67
+ <% end %>
68
+ |====
69
+ <% end %>
70
+ <% end %>
71
+
72
+ <%if feature.key?('scenarios') %>
73
+ <% feature['scenarios'].each do |scenario| %>
74
+ ==== [scenario name]#<%= scenario['name'] %>#
75
+ <%if scenario.key?('description') %>
76
+ <%= scenario['description'] %>
77
+ <% end %>
78
+ <%if scenario.key?('steps') %>[.step-list]<% scenario['steps'].each do |step| %>
79
+ * *<%= step['keyword'].strip %>* <%= step['name'] %>
80
+ <%if step.key?('doc_string') %>
81
+ [source,gherkin]
82
+ ----
83
+ <%= step['doc_string']['value'] %>
84
+ ----
85
+ <% end %>
86
+ <%if step.key?('rows') %>
87
+ +<% if step['rows'].first && step['rows'].first.key?('cols') %>
88
+ [<%= step['rows'].first['cols'] %>]
89
+ <% end %>
90
+ |====
91
+ <% step['rows'].each_with_index do |row, index| %>
92
+ <% row['cells'].each_with_index do |cell, i| %>
93
+ <% if row.key?('cell-styles') && row['cell-styles'].length > i %>
94
+ <%= row['cell-styles'][i] %>
95
+ <% end %>
96
+ | <%= cell %>
97
+ <% end %>
98
+ <% end %>|====
99
+ <% end %>
100
+ <% end %>
101
+ <% end %>
102
+
103
+ <%if scenario.key?('examples')
104
+ example = scenario['examples'] %>
105
+ ===== [example name]#<%= example['keyword'].strip %> <%= example['name'] %>#
106
+
107
+ <% if example['rows'].first && example['rows'].first.key?('cols') %>
108
+ [<%= example['rows'].first['cols'] %>]
109
+ <% end %>
110
+ |====
111
+ <% example['rows'].each_with_index do |row, index| %>
112
+ <% row['cells'].each_with_index do |cell, i| %>
113
+ <% if row.key?('cell-styles') && row['cell-styles'].length > i %><%= row['cell-styles'][i] %><% end %>
114
+ | <%= cell %>
115
+ <% end %> <%# cell %>
116
+ <% if index == 0 %>
117
+ <% end %>
118
+ <% end %>|====
119
+ <% end %>
120
+ <% end %>
121
+ <% end %>
@@ -0,0 +1,24 @@
1
+ = icon:cogs[] Brine Specifications
2
+ Matt Whipple <http://github.com/mwhipple[@mwhipple]>
3
+ :description: Specifications for Brine
4
+ :keywords: Brine, Cucumber, RESt, DSL
5
+
6
+ == Request Construction
7
+ gherkin::../../features/request_construction/basic.feature[spec.erb]
8
+ gherkin::../../features/request_construction/body.feature[spec.erb]
9
+ gherkin::../../features/request_construction/params.feature[spec.erb]
10
+ gherkin::../../features/request_construction/headers.feature[spec.erb]
11
+ gherkin::../../features/request_construction/clearing.feature[spec.erb]
12
+
13
+ == Resource Cleanup
14
+ gherkin::../../features/resource_cleanup/cleanup.feature[spec.erb]
15
+
16
+ == Selection
17
+ gherkin::../../features/selectors/any.feature[spec.erb]
18
+ gherkin::../../features/selectors/all.feature[spec.erb]
19
+
20
+ == Assertion
21
+ gherkin::../../features/assertions/is_equal_to.feature[spec.erb]
22
+ gherkin::../../features/assertions/is_matching.feature[spec.erb]
23
+ gherkin::../../features/assertions/is_including.feature[spec.erb]
24
+ gherkin::../../features/assertions/is_a_valid.feature[spec.erb]