brine-dsl 0.8.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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`
|