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