brine-dsl 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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]