brine-dsl 0.5.0 → 0.6.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/.gitignore +0 -1
- data/Gemfile.lock +6 -4
- data/README.md +2 -132
- data/Rakefile +3 -0
- data/brine-dsl.gemspec +1 -1
- data/docs/cookbook.html +73 -5
- data/docs/guide.html +13 -13
- data/docs/index.html +1 -1
- data/docs/specs.html +101 -7
- data/docs/src/cookbook.adoc +56 -0
- data/docs/src/guide.adoc +12 -13
- data/docs/src/specs.adoc +2 -1
- data/features/assertions/is_empty.feature +67 -0
- data/features/assertions/is_equal_to.feature +2 -2
- data/features/assertions/is_matching.feature +4 -4
- data/lib/brine/requester.rb +24 -13
- data/lib/brine/step_definitions/assertions.rb +7 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f3a218b02acd1fc90e2ca605c74ce54a37b9b05a
|
4
|
+
data.tar.gz: 7919bf69e899db000df3bdb3c682875e02d1df32
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52d2907d296a7794f619adbd6dfafabe45aa6d572c50d0520edea5310c47fbc91eed1c937b73551960ef1f89da8d35a7239bfa7f618e44bf9c095eec6c197430
|
7
|
+
data.tar.gz: 31b7748a12f98aa942d21e6e58ac7c26ed014bbf3c66bc9bb19308568fc52b70b8d62209fa8ab5fdfe6ed3d7b0743da889ad1910264a2761526c92ba0f2e7e14
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
brine-dsl (0.
|
4
|
+
brine-dsl (0.6.0.pre.SNAPSHOT)
|
5
5
|
cucumber (~> 2.4)
|
6
6
|
faraday
|
7
7
|
faraday_middleware
|
@@ -59,8 +59,9 @@ GEM
|
|
59
59
|
cucumber (~> 2.0)
|
60
60
|
guard-compat (~> 1.0)
|
61
61
|
nenv (~> 0.1)
|
62
|
-
jsonpath (0.8.
|
62
|
+
jsonpath (0.8.11)
|
63
63
|
multi_json
|
64
|
+
to_regexp (~> 0.2.1)
|
64
65
|
jwt (1.5.6)
|
65
66
|
listen (3.1.5)
|
66
67
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
@@ -86,7 +87,7 @@ GEM
|
|
86
87
|
pry (0.11.3)
|
87
88
|
coderay (~> 1.1.0)
|
88
89
|
method_source (~> 0.9.0)
|
89
|
-
rack (2.0.
|
90
|
+
rack (2.0.4)
|
90
91
|
rake (12.3.0)
|
91
92
|
rb-fsevent (0.10.2)
|
92
93
|
rb-inotify (0.9.10)
|
@@ -95,7 +96,7 @@ GEM
|
|
95
96
|
rspec-core (~> 3.7.0)
|
96
97
|
rspec-expectations (~> 3.7.0)
|
97
98
|
rspec-mocks (~> 3.7.0)
|
98
|
-
rspec-core (3.7.
|
99
|
+
rspec-core (3.7.1)
|
99
100
|
rspec-support (~> 3.7.0)
|
100
101
|
rspec-expectations (3.7.0)
|
101
102
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -107,6 +108,7 @@ GEM
|
|
107
108
|
ruby_dep (1.5.0)
|
108
109
|
shellany (0.0.1)
|
109
110
|
thor (0.20.0)
|
111
|
+
to_regexp (0.2.1)
|
110
112
|
|
111
113
|
PLATFORMS
|
112
114
|
ruby
|
data/README.md
CHANGED
@@ -3,135 +3,5 @@ Brine
|
|
3
3
|
|
4
4
|
> Cucumber DSL for testing REST APIs
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
REpresentational State Transfer APIs expose their functionality
|
9
|
-
through combinations of fairly coarse primitives that generally
|
10
|
-
revolve around the use of transferring data in a standard exchange
|
11
|
-
format (such as JSON) using HTTP methods and other aspects of the very
|
12
|
-
simple HTTP protocol. Tests for such an API can therefore be defined
|
13
|
-
using a domain specific language (DSL) built around those higher level
|
14
|
-
ideas rather than requiring a general purpose language (the equivalent
|
15
|
-
of scripted `curl` commands with some glue code and assertions).
|
16
|
-
This project provides such a DSL by using select libraries
|
17
|
-
integrated into Cucumber, where Cucumber provides a test-oriented
|
18
|
-
framework for DSL creation.
|
19
|
-
|
20
|
-
Sample Usage
|
21
|
-
---
|
22
|
-
The general usage pattern revolves around construction of a request
|
23
|
-
and performing assertions against the received response.
|
24
|
-
|
25
|
-
```gherkin
|
26
|
-
When the request body is assigned:
|
27
|
-
"""
|
28
|
-
{"first_name": "John",
|
29
|
-
"last_name": "Smith"}
|
30
|
-
"""
|
31
|
-
And a POST is sent to `/users`
|
32
|
-
Then the value of the response status is equal to `200`
|
33
|
-
And the value of the response body is including:
|
34
|
-
"""
|
35
|
-
{"first_name": "John",
|
36
|
-
"last_name": "Smith"}
|
37
|
-
"""
|
38
|
-
```
|
39
|
-
|
40
|
-
Disclaimer
|
41
|
-
---
|
42
|
-
Right now this project is new and features are being implemented as
|
43
|
-
needed rather than speculatively. Some of the info here may be more
|
44
|
-
optimistic than realistic until things are smoothed out or hopes abandoned.
|
45
|
-
|
46
|
-
Features
|
47
|
-
---
|
48
|
-
|
49
|
-
### Variable Binding/Expansion
|
50
|
-
|
51
|
-
In cases where dynamic data is in the response or is desired for the
|
52
|
-
request, then values can be bound to identifiers which can then be
|
53
|
-
expanded using [Mustache](mustache.github.io) templates in your
|
54
|
-
feature files.
|
55
|
-
|
56
|
-
### Type Transforms
|
57
|
-
|
58
|
-
Different types of data can be expressed directly in the feature files
|
59
|
-
or expanded into variables by using the appropriate syntax for that
|
60
|
-
type.
|
61
|
-
|
62
|
-
[Read More](https://github.com/brightcove/brine/wiki/Argument-Transforms)
|
63
|
-
|
64
|
-
#### Type Coercion
|
65
|
-
|
66
|
-
Related to transforms, a facility to coerce types is also provided. This allows
|
67
|
-
more intelligent comparison of inputs which have been transformed to a
|
68
|
-
richer data type with those that have not been transformed (normally strings).
|
69
|
-
As an example comparing a date/time value with a string will attempt to parse
|
70
|
-
the string to a date/time so that the values can be compared using the proper semantics.
|
71
|
-
|
72
|
-
### Resource Cleanup
|
73
|
-
|
74
|
-
Tests are likely to create resources which should then be cleaned up,
|
75
|
-
restoring the pre-test state of the system: steps to facilitate this
|
76
|
-
are provided.
|
77
|
-
|
78
|
-
### Authentication
|
79
|
-
|
80
|
-
Presently OAuth2 is supported to issue authenticated requests during a
|
81
|
-
test (likely using a feature `Background`).
|
82
|
-
|
83
|
-
### Division of Selection and Assertion
|
84
|
-
|
85
|
-
To allow for a wider range of tests without an exploding code base
|
86
|
-
(and keeping the assertions easier to write), Brine internall splits the
|
87
|
-
selection of the value(s) to test from the assertion(s) that will be performed against it.
|
88
|
-
|
89
|
-
[Read Mode](https://github.com/brightcove/brine/wiki/Selection-and-Assertion)
|
90
|
-
|
91
|
-
### Request Construction and Response Assertion Step Definitions
|
92
|
-
|
93
|
-
The previous features combined with the library of provide steps should
|
94
|
-
cover all of the functionality needed to exercise and validate all of
|
95
|
-
the functionality exposed by your REST API.
|
96
|
-
|
97
|
-
|
98
|
-
Installation
|
99
|
-
---
|
100
|
-
|
101
|
-
Presently the gem for this project isn't being published anywhere:
|
102
|
-
primarily because tracking down a local gem repository seems scary. If
|
103
|
-
this project is open sourced then this will probably change but in the
|
104
|
-
meantime the project can be used off of GitHub by adding this to your
|
105
|
-
`Gemfile` and performing the usual `bundle install` dance:
|
106
|
-
|
107
|
-
```ruby
|
108
|
-
git 'git@github.com:brightcove/brine.git', :branch => 'master' do
|
109
|
-
gem 'brine'
|
110
|
-
end
|
111
|
-
```
|
112
|
-
|
113
|
-
Specific branches and refs can be targetted as
|
114
|
-
documented [here](http://bundler.io/git.html). This should likely be
|
115
|
-
done in any cases where you're not actively tracking Brine and don't want
|
116
|
-
your tests to suddenly break because of changes to it.
|
117
|
-
|
118
|
-
Brine can then be "mixed in" to your project (which adds assorted
|
119
|
-
modules to the `World` and loads all the step definitions and other
|
120
|
-
Cucumber magic) by adding the following to your `support/env.rb` or
|
121
|
-
other ruby file:
|
122
|
-
|
123
|
-
```ruby
|
124
|
-
require 'brine'
|
125
|
-
|
126
|
-
World(brine_mix)
|
127
|
-
```
|
128
|
-
|
129
|
-
Select pieces can also be loaded (to be documented). With the above,
|
130
|
-
feature files should be able to be written and executed without
|
131
|
-
requiring any additional ruby code.
|
132
|
-
|
133
|
-
Questions? Comments?
|
134
|
-
---
|
135
|
-
Check out the [wiki](https://github.com/brightcove/brine/wiki) for more information
|
136
|
-
and search for related [issues](https://github.com/brightcove/brine/issues)
|
137
|
-
or open one for anything not documented or implemented elsewhere.
|
6
|
+
Documentation is hosted at:
|
7
|
+
https://brightcove.github.io/brine/
|
data/Rakefile
CHANGED
data/brine-dsl.gemspec
CHANGED
data/docs/cookbook.html
CHANGED
@@ -427,13 +427,20 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
|
|
427
427
|
<div id="toctitle">Table of Contents</div>
|
428
428
|
<ul class="sectlevel1">
|
429
429
|
<li><a href="#_overview">Overview</a></li>
|
430
|
-
<li><a href="#
|
430
|
+
<li><a href="#_intercepting_of_http_calls">Intercepting of HTTP Calls</a>
|
431
431
|
<ul class="sectlevel2">
|
432
432
|
<li><a href="#_context">Context</a></li>
|
433
433
|
<li><a href="#_solution">Solution</a></li>
|
434
434
|
<li><a href="#_recipe">Recipe</a></li>
|
435
435
|
</ul>
|
436
436
|
</li>
|
437
|
+
<li><a href="#__assurances">"Assurances"</a>
|
438
|
+
<ul class="sectlevel2">
|
439
|
+
<li><a href="#_context_2">Context</a></li>
|
440
|
+
<li><a href="#_solution_2">Solution</a></li>
|
441
|
+
<li><a href="#_recipe_2">Recipe</a></li>
|
442
|
+
</ul>
|
443
|
+
</li>
|
437
444
|
</ul>
|
438
445
|
</div>
|
439
446
|
</div>
|
@@ -447,14 +454,75 @@ Brine but are not considered part of Brine’s responsibility (at least pres
|
|
447
454
|
Many of these are focused on simplicity and ease rather than robustness or elegance,
|
448
455
|
and modification to address specific cases should be expected.</p>
|
449
456
|
</div>
|
457
|
+
<div class="paragraph">
|
458
|
+
<p>Many of these will also be platform specific and be subject to further organization
|
459
|
+
once platforms beyond ruby are supported.</p>
|
460
|
+
</div>
|
450
461
|
</div>
|
451
462
|
</div>
|
452
463
|
<div class="sect1">
|
453
|
-
<h2 id="
|
464
|
+
<h2 id="_intercepting_of_http_calls">Intercepting of HTTP Calls</h2>
|
454
465
|
<div class="sectionbody">
|
455
466
|
<div class="sect2">
|
456
467
|
<h3 id="_context">Context</h3>
|
457
468
|
<div class="paragraph">
|
469
|
+
<p>There may be cases where the request or reponse may need to be modified
|
470
|
+
in some way before or after transmission, for example to add a custom header
|
471
|
+
which is somehow specific to the testing configuration and therefore outside
|
472
|
+
of anything that belongs in the specification.</p>
|
473
|
+
</div>
|
474
|
+
</div>
|
475
|
+
<div class="sect2">
|
476
|
+
<h3 id="_solution">Solution</h3>
|
477
|
+
<div class="paragraph">
|
478
|
+
<p>This can be done through the registration of custom Faraday middleware on
|
479
|
+
the Brine client. All of the registered middleware attaches
|
480
|
+
to the generated connection through an array of functions in
|
481
|
+
<a href="https://github.com/brightcove/brine/blob/master/lib/brine/requester.rb">requester.rb</a>.
|
482
|
+
Each function accepts the connection object as its single parameter. The array of functions
|
483
|
+
is exposed as <code>connection_handlers</code> and can be modified through getting that property
|
484
|
+
from either the default <code>World</code> (for the default client) or from an appropriate <code>ClientBuilder</code>
|
485
|
+
if creating custom clients and mutating it accordingly (setting the reference is not suported).
|
486
|
+
You could just build your own client without the facilities provided by Brine.</p>
|
487
|
+
</div>
|
488
|
+
</div>
|
489
|
+
<div class="sect2">
|
490
|
+
<h3 id="_recipe">Recipe</h3>
|
491
|
+
<div class="paragraph">
|
492
|
+
<p>An example to add a custom header could be implemented as follows:</p>
|
493
|
+
</div>
|
494
|
+
<div class="listingblock">
|
495
|
+
<div class="content">
|
496
|
+
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">require 'faraday'
|
497
|
+
class CustomHeaderAdder < Faraday::Middleware
|
498
|
+
def initialize(app, key, val)
|
499
|
+
super(app)
|
500
|
+
@key = key
|
501
|
+
@val = val
|
502
|
+
end
|
503
|
+
|
504
|
+
def call(env)
|
505
|
+
env[:request_headers].merge!({@key => @val})
|
506
|
+
@app.call(env)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
...
|
511
|
+
|
512
|
+
connection_handlers.unshift(proc do |conn|
|
513
|
+
conn.use CustomHeaderAdder, 'x-header', 'I am a test'
|
514
|
+
end)</code></pre>
|
515
|
+
</div>
|
516
|
+
</div>
|
517
|
+
</div>
|
518
|
+
</div>
|
519
|
+
</div>
|
520
|
+
<div class="sect1">
|
521
|
+
<h2 id="__assurances">"Assurances"</h2>
|
522
|
+
<div class="sectionbody">
|
523
|
+
<div class="sect2">
|
524
|
+
<h3 id="_context_2">Context</h3>
|
525
|
+
<div class="paragraph">
|
458
526
|
<p>Ideally all tests should be as self-contained and isolated as possible;
|
459
527
|
when writing functional tests, however, there are cases where this isn’t
|
460
528
|
feasible or possible. In some cases a system depends on another external
|
@@ -474,7 +542,7 @@ complete test isolation.</p>
|
|
474
542
|
</div>
|
475
543
|
</div>
|
476
544
|
<div class="sect2">
|
477
|
-
<h3 id="
|
545
|
+
<h3 id="_solution_2">Solution</h3>
|
478
546
|
<div class="paragraph">
|
479
547
|
<p>In such cases a standard solution is to designate certain resources to be reused for
|
480
548
|
certain tests. These are analogous to the concept of "fixtures" in some test suites
|
@@ -517,7 +585,7 @@ up test runs through the use of parallelism/asynchronicity.</p>
|
|
517
585
|
</div>
|
518
586
|
</div>
|
519
587
|
<div class="sect2">
|
520
|
-
<h3 id="
|
588
|
+
<h3 id="_recipe_2">Recipe</h3>
|
521
589
|
<div class="paragraph">
|
522
590
|
<p>This can be done using standard cucumber tags. Assurances can be defined in designated
|
523
591
|
<code>assure_*.feature</code> files where each Feature is appropriately tagged:</p>
|
@@ -560,7 +628,7 @@ Cucumber behavior so that the full test suite can be more stochastic
|
|
560
628
|
</div>
|
561
629
|
<div id="footer">
|
562
630
|
<div id="footer-text">
|
563
|
-
Last updated
|
631
|
+
Last updated 2018-02-08 14:37:47 EST
|
564
632
|
</div>
|
565
633
|
</div>
|
566
634
|
</body>
|
data/docs/guide.html
CHANGED
@@ -578,24 +578,18 @@ the functionality exposed by your REST API.</p>
|
|
578
578
|
<h2 id="_installation">Installation</h2>
|
579
579
|
<div class="sectionbody">
|
580
580
|
<div class="paragraph">
|
581
|
-
<p>
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
<code>Gemfile</code> and performing the usual <code>bundle install</code> dance:</p>
|
581
|
+
<p>Brine is published as <code>brine-dsl</code> on rubygems, the page for which is
|
582
|
+
at <a href="https://rubygems.org/gems/brine-dsl" class="bare">https://rubygems.org/gems/brine-dsl</a>. The latest version and other
|
583
|
+
gem metadata can be viewed on that page. Brine can be used by
|
584
|
+
declaring that gem in your project Gemfile such as:</p>
|
586
585
|
</div>
|
587
586
|
<div class="listingblock">
|
588
587
|
<div class="content">
|
589
|
-
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">
|
590
|
-
gem 'brine'
|
591
|
-
end</code></pre>
|
588
|
+
<pre class="highlightjs highlight"><code class="language-ruby" data-lang="ruby">gem 'brine-dsl', '~> 1.0'</code></pre>
|
592
589
|
</div>
|
593
590
|
</div>
|
594
591
|
<div class="paragraph">
|
595
|
-
<p>
|
596
|
-
documented <a href="http://bundler.io/git.html">here</a>. This should likely be
|
597
|
-
done in any cases where you’re not actively tracking Brine and don’t want
|
598
|
-
your tests to suddenly break because of changes to it.</p>
|
592
|
+
<p>(after version 1.0 is released).</p>
|
599
593
|
</div>
|
600
594
|
<div class="paragraph">
|
601
595
|
<p>Brine can then be "mixed in" to your project (which adds assorted
|
@@ -1105,6 +1099,12 @@ which the step was evaluated.</p>
|
|
1105
1099
|
<dd>
|
1106
1100
|
<p>Assert that the current select value includes/is a superset of <code>VALUE</code>.</p>
|
1107
1101
|
</dd>
|
1102
|
+
<dt class="hdlist1"><code>Then it is empty</code></dt>
|
1103
|
+
<dd>
|
1104
|
+
<p> Assert that value is empty or null. Any type which is not testable for emptiness
|
1105
|
+
(such as booleans or numbers) will always return false. Null is treated as an empty
|
1106
|
+
value so that it can be treated as such for endpoints that return null in place of empty collections, and non-null empty values can easily be tested for using conjunction.</p>
|
1107
|
+
</dd>
|
1108
1108
|
<dt class="hdlist1"><code>Then it is a valid `$TYPE`</code></dt>
|
1109
1109
|
<dd>
|
1110
1110
|
<p> Assert that the selected value is a valid instance of a <code>TYPE</code>. Presently this
|
@@ -1142,7 +1142,7 @@ wiring and documentation. The current supported types are:</p>
|
|
1142
1142
|
</div>
|
1143
1143
|
<div id="footer">
|
1144
1144
|
<div id="footer-text">
|
1145
|
-
Last updated
|
1145
|
+
Last updated 2018-02-08 12:09:12 EST
|
1146
1146
|
</div>
|
1147
1147
|
</div>
|
1148
1148
|
</body>
|
data/docs/index.html
CHANGED
@@ -465,7 +465,7 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
|
|
465
465
|
</div>
|
466
466
|
<div id="footer">
|
467
467
|
<div id="footer-text">
|
468
|
-
Last updated 2017-10-
|
468
|
+
Last updated 2017-10-17 16:14:35 EDT
|
469
469
|
</div>
|
470
470
|
</div>
|
471
471
|
</body>
|
data/docs/specs.html
CHANGED
@@ -451,6 +451,7 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
|
|
451
451
|
<li><a href="#__span_class_feature_name_equal_to_span"><span class="feature name">Equal to</span></a></li>
|
452
452
|
<li><a href="#__span_class_feature_name_matching_span"><span class="feature name">Matching</span></a></li>
|
453
453
|
<li><a href="#__span_class_feature_name_including_span"><span class="feature name">Including</span></a></li>
|
454
|
+
<li><a href="#__span_class_feature_name_empty_span"><span class="feature name">Empty</span></a></li>
|
454
455
|
<li><a href="#__span_class_feature_name_a_valid_span"><span class="feature name">A valid …​</span></a></li>
|
455
456
|
</ul>
|
456
457
|
</li>
|
@@ -1247,10 +1248,10 @@ Then the value of the response body children `..val` has elements which are all
|
|
1247
1248
|
<div class="sect2">
|
1248
1249
|
<h3 id="__span_class_feature_name_equal_to_span"><span class="feature name">Equal to</span></h3>
|
1249
1250
|
<div class="paragraph">
|
1250
|
-
<p>It can be asserted that a value is equal to another value
|
1251
|
+
<p>It can be asserted that a value is equal to another value.</p>
|
1251
1252
|
</div>
|
1252
1253
|
<div class="sect3">
|
1253
|
-
<h4 id="
|
1254
|
+
<h4 id="__span_class_scenario_name_assorted_positive_and_negative_assertions_pass_span"><span class="scenario name">Assorted positive and negative assertions pass.</span></h4>
|
1254
1255
|
<div class="ulist step-list">
|
1255
1256
|
<ul>
|
1256
1257
|
<li>
|
@@ -1334,10 +1335,10 @@ Examples:
|
|
1334
1335
|
<div class="sect2">
|
1335
1336
|
<h3 id="__span_class_feature_name_matching_span"><span class="feature name">Matching</span></h3>
|
1336
1337
|
<div class="paragraph">
|
1337
|
-
<p>It can be asserted that a value matches another string or regex
|
1338
|
+
<p>It can be asserted that a value matches another string or regex.</p>
|
1338
1339
|
</div>
|
1339
1340
|
<div class="sect3">
|
1340
|
-
<h4 id="
|
1341
|
+
<h4 id="__span_class_scenario_name_assorted_positive_and_negative_assertions_span_3"><span class="scenario name">Assorted positive and negative assertions.</span></h4>
|
1341
1342
|
<div class="ulist step-list">
|
1342
1343
|
<ul>
|
1343
1344
|
<li>
|
@@ -1347,8 +1348,8 @@ Examples:
|
|
1347
1348
|
</div>
|
1348
1349
|
<div class="listingblock">
|
1349
1350
|
<div class="content">
|
1350
|
-
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: Assert value matchiness
|
1351
|
-
Scenario: String in response body matched against a regex
|
1351
|
+
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: Assert value matchiness.
|
1352
|
+
Scenario: String in response body is matched against a regex.
|
1352
1353
|
When the response body is assigned:
|
1353
1354
|
"""
|
1354
1355
|
http://www.github.com?var=val
|
@@ -1452,6 +1453,99 @@ And the value of the response body is not including:
|
|
1452
1453
|
</div>
|
1453
1454
|
</div>
|
1454
1455
|
<div class="sect2">
|
1456
|
+
<h3 id="__span_class_feature_name_empty_span"><span class="feature name">Empty</span></h3>
|
1457
|
+
<div class="paragraph">
|
1458
|
+
<p>It can be asserted that a value is empty.</p>
|
1459
|
+
</div>
|
1460
|
+
<div class="sect3">
|
1461
|
+
<h4 id="__span_class_scenario_name_assorted_positive_and_negative_assertions_pass_span_2"><span class="scenario name">Assorted positive and negative assertions pass.</span></h4>
|
1462
|
+
<div class="ulist step-list">
|
1463
|
+
<ul>
|
1464
|
+
<li>
|
1465
|
+
<p><strong>Given</strong> a file named "features/is_empty.feature" with:</p>
|
1466
|
+
</li>
|
1467
|
+
</ul>
|
1468
|
+
</div>
|
1469
|
+
<div class="listingblock">
|
1470
|
+
<div class="content">
|
1471
|
+
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">Feature: Assert emptiness for multiple types.
|
1472
|
+
Scenario: Empty body is empty.
|
1473
|
+
When the response body is assigned ``
|
1474
|
+
Then the value of the response body is empty
|
1475
|
+
|
1476
|
+
Scenario: Whitespace-only body is empty.
|
1477
|
+
When the response body is assigned:
|
1478
|
+
"""
|
1479
|
+
|
1480
|
+
"""
|
1481
|
+
Then the value of the response body is empty
|
1482
|
+
|
1483
|
+
Scenario: Empty string is empty.
|
1484
|
+
When the response body is assigned `""`
|
1485
|
+
Then the value of the response body is empty
|
1486
|
+
|
1487
|
+
Scenario: Non-empty string is not empty.
|
1488
|
+
When the response body is assigned `blah`
|
1489
|
+
Then the value of the response body is not empty
|
1490
|
+
|
1491
|
+
Scenario: Quoted whitespace is not empty.
|
1492
|
+
When the response body is assigned `" "`
|
1493
|
+
Then the value of the response body is not empty
|
1494
|
+
|
1495
|
+
Scenario: Empty arrays are empty.
|
1496
|
+
When the response body is assigned `[]`
|
1497
|
+
Then the value of the response body is empty
|
1498
|
+
|
1499
|
+
Scenario: Non-empty arrays are not empty.
|
1500
|
+
When the response body is assigned `[[]]`
|
1501
|
+
Then the value of the response body is not empty
|
1502
|
+
|
1503
|
+
Scenario: Empty objects are empty.
|
1504
|
+
When the response body is assigned `{}`
|
1505
|
+
Then the value of the response body is empty
|
1506
|
+
|
1507
|
+
Scenario: Non-empty objects are not empty.
|
1508
|
+
When the response body is assigned `{"foo":{}}`
|
1509
|
+
Then the value of the response body is not empty
|
1510
|
+
|
1511
|
+
Scenario: Null values are empty.
|
1512
|
+
When the response body is assigned `{"foo": null}`
|
1513
|
+
Then the value of the response body child `foo` is empty
|
1514
|
+
|
1515
|
+
Scenario: False is not empty.
|
1516
|
+
When the response body is assigned `false`
|
1517
|
+
Then the value of the response body is not empty
|
1518
|
+
|
1519
|
+
Scenario: 0 is not empty.
|
1520
|
+
When the response body is assigned `0`
|
1521
|
+
Then the value of the response body is not empty</code></pre>
|
1522
|
+
</div>
|
1523
|
+
</div>
|
1524
|
+
<div class="ulist">
|
1525
|
+
<ul>
|
1526
|
+
<li>
|
1527
|
+
<p><strong>When</strong> I run <code>cucumber --strict features/is_empty.feature</code></p>
|
1528
|
+
</li>
|
1529
|
+
<li>
|
1530
|
+
<p><strong>Then</strong> the output should contain:</p>
|
1531
|
+
</li>
|
1532
|
+
</ul>
|
1533
|
+
</div>
|
1534
|
+
<div class="listingblock">
|
1535
|
+
<div class="content">
|
1536
|
+
<pre class="highlightjs highlight"><code class="language-gherkin" data-lang="gherkin">12 passed</code></pre>
|
1537
|
+
</div>
|
1538
|
+
</div>
|
1539
|
+
<div class="ulist">
|
1540
|
+
<ul>
|
1541
|
+
<li>
|
1542
|
+
<p><strong>And</strong> it should pass</p>
|
1543
|
+
</li>
|
1544
|
+
</ul>
|
1545
|
+
</div>
|
1546
|
+
</div>
|
1547
|
+
</div>
|
1548
|
+
<div class="sect2">
|
1455
1549
|
<h3 id="__span_class_feature_name_a_valid_span"><span class="feature name">A valid …​</span></h3>
|
1456
1550
|
<div class="sect3">
|
1457
1551
|
<h4 id="__span_class_scenario_name_positive_and_negative_assertions_for_json_types_span"><span class="scenario name">Positive and negative assertions for JSON types.</span></h4>
|
@@ -1665,7 +1759,7 @@ Then the value of the response body children `.val` has elements which are all a
|
|
1665
1759
|
</div>
|
1666
1760
|
<div id="footer">
|
1667
1761
|
<div id="footer-text">
|
1668
|
-
Last updated
|
1762
|
+
Last updated 2018-02-08 12:09:12 EST
|
1669
1763
|
</div>
|
1670
1764
|
</div>
|
1671
1765
|
</body>
|
data/docs/src/cookbook.adoc
CHANGED
@@ -4,13 +4,67 @@ Matt Whipple <http://github.com/mwhipple[@mwhipple]>
|
|
4
4
|
:keywords: Brine, Cucumber, REST, DSL
|
5
5
|
|
6
6
|
== Overview
|
7
|
+
|
7
8
|
The following are some recipes to address issues which may arise while using
|
8
9
|
Brine but are not considered part of Brine's responsibility (at least presently).
|
9
10
|
Many of these are focused on simplicity and ease rather than robustness or elegance,
|
10
11
|
and modification to address specific cases should be expected.
|
11
12
|
|
13
|
+
Many of these will also be platform specific and be subject to further organization
|
14
|
+
once platforms beyond ruby are supported.
|
15
|
+
|
16
|
+
== Intercepting of HTTP Calls
|
17
|
+
|
18
|
+
=== Context
|
19
|
+
|
20
|
+
There may be cases where the request or reponse may need to be modified
|
21
|
+
in some way before or after transmission, for example to add a custom header
|
22
|
+
which is somehow specific to the testing configuration and therefore outside
|
23
|
+
of anything that belongs in the specification.
|
24
|
+
|
25
|
+
=== Solution
|
26
|
+
|
27
|
+
This can be done through the registration of custom Faraday middleware on
|
28
|
+
the Brine client. All of the registered middleware attaches
|
29
|
+
to the generated connection through an array of functions in
|
30
|
+
https://github.com/brightcove/brine/blob/master/lib/brine/requester.rb[requester.rb].
|
31
|
+
Each function accepts the connection object as its single parameter. The array of functions
|
32
|
+
is exposed as `connection_handlers` and can be modified through getting that property
|
33
|
+
from either the default `World` (for the default client) or from an appropriate `ClientBuilder`
|
34
|
+
if creating custom clients and mutating it accordingly (setting the reference is not suported).
|
35
|
+
You could just build your own client without the facilities provided by Brine.
|
36
|
+
|
37
|
+
=== Recipe
|
38
|
+
|
39
|
+
An example to add a custom header could be implemented as follows:
|
40
|
+
|
41
|
+
[source,ruby]
|
42
|
+
----
|
43
|
+
require 'faraday'
|
44
|
+
class CustomHeaderAdder < Faraday::Middleware
|
45
|
+
def initialize(app, key, val)
|
46
|
+
super(app)
|
47
|
+
@key = key
|
48
|
+
@val = val
|
49
|
+
end
|
50
|
+
|
51
|
+
def call(env)
|
52
|
+
env[:request_headers].merge!({@key => @val})
|
53
|
+
@app.call(env)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
...
|
58
|
+
|
59
|
+
connection_handlers.unshift(proc do |conn|
|
60
|
+
conn.use CustomHeaderAdder, 'x-header', 'I am a test'
|
61
|
+
end)
|
62
|
+
----
|
63
|
+
|
12
64
|
== "Assurances"
|
65
|
+
|
13
66
|
=== Context
|
67
|
+
|
14
68
|
Ideally all tests should be as self-contained and isolated as possible;
|
15
69
|
when writing functional tests, however, there are cases where this isn't
|
16
70
|
feasible or possible. In some cases a system depends on another external
|
@@ -28,6 +82,7 @@ and the number of accounts is limited, then that is likely to unfortunately prev
|
|
28
82
|
complete test isolation.
|
29
83
|
|
30
84
|
=== Solution
|
85
|
+
|
31
86
|
In such cases a standard solution is to designate certain resources to be reused for
|
32
87
|
certain tests. These are analogous to the concept of "fixtures" in some test suites
|
33
88
|
though there may be slight differences in implementation and reliance on them.
|
@@ -41,6 +96,7 @@ validate that preconditions are met; ideally if such preconditions can be establ
|
|
41
96
|
idempotently then the assurances can do so before the validation.
|
42
97
|
|
43
98
|
==== Assurances are NOT Tests
|
99
|
+
|
44
100
|
**Assurances validate a state which is desired to be consistently retained within the
|
45
101
|
system rather than being changed**. This means that they should _not_ be used for tests
|
46
102
|
as that would require state changes, nor should they clean up after themselves (as that
|
data/docs/src/guide.adoc
CHANGED
@@ -75,23 +75,17 @@ cover all of the functionality needed to exercise and validate all of
|
|
75
75
|
the functionality exposed by your REST API.
|
76
76
|
|
77
77
|
== Installation
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
`Gemfile` and performing the usual `bundle install` dance:
|
78
|
+
Brine is published as `brine-dsl` on rubygems, the page for which is
|
79
|
+
at https://rubygems.org/gems/brine-dsl. The latest version and other
|
80
|
+
gem metadata can be viewed on that page. Brine can be used by
|
81
|
+
declaring that gem in your project Gemfile such as:
|
83
82
|
|
84
83
|
[source,ruby]
|
85
84
|
----
|
86
|
-
|
87
|
-
gem 'brine'
|
88
|
-
end
|
85
|
+
gem 'brine-dsl', '~> 1.0'
|
89
86
|
----
|
90
87
|
|
91
|
-
|
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.
|
88
|
+
(after version 1.0 is released).
|
95
89
|
|
96
90
|
Brine can then be "mixed in" to your project (which adds assorted
|
97
91
|
modules to the `World` and loads all the step definitions and other
|
@@ -414,6 +408,11 @@ _see <<_selection_and_assertion>>_
|
|
414
408
|
`Then it is including {grave}$VALUE{grave}`::
|
415
409
|
Assert that the current select value includes/is a superset of `VALUE`.
|
416
410
|
|
411
|
+
`Then it is empty`::
|
412
|
+
Assert that value is empty or null. Any type which is not testable for emptiness
|
413
|
+
(such as booleans or numbers) will always return false. Null is treated as an empty
|
414
|
+
value so that it can be treated as such for endpoints that return null in place of empty collections, and non-null empty values can easily be tested for using conjunction.
|
415
|
+
|
417
416
|
`Then it is a valid {grave}$TYPE{grave}`::
|
418
417
|
Assert that the selected value is a valid instance of a `TYPE`. Presently this
|
419
418
|
is focused on standard data types (intially based on those specified by JSON),
|
@@ -424,4 +423,4 @@ wiring and documentation. The current supported types are:
|
|
424
423
|
* `Number`
|
425
424
|
* `Integer`
|
426
425
|
* `Array`
|
427
|
-
* `Boolean`
|
426
|
+
* `Boolean`
|
data/docs/src/specs.adoc
CHANGED
@@ -21,4 +21,5 @@ gherkin::../../features/selectors/all.feature[spec.erb]
|
|
21
21
|
gherkin::../../features/assertions/is_equal_to.feature[spec.erb]
|
22
22
|
gherkin::../../features/assertions/is_matching.feature[spec.erb]
|
23
23
|
gherkin::../../features/assertions/is_including.feature[spec.erb]
|
24
|
-
gherkin::../../features/assertions/
|
24
|
+
gherkin::../../features/assertions/is_empty.feature[spec.erb]
|
25
|
+
gherkin::../../features/assertions/is_a_valid.feature[spec.erb]
|
@@ -0,0 +1,67 @@
|
|
1
|
+
Feature: Empty
|
2
|
+
It can be asserted that a value is empty.
|
3
|
+
|
4
|
+
Scenario: Assorted positive and negative assertions pass.
|
5
|
+
Given a file named "features/is_empty.feature" with:
|
6
|
+
"""
|
7
|
+
|
8
|
+
Feature: Assert emptiness for multiple types.
|
9
|
+
Scenario: Empty body is empty.
|
10
|
+
When the response body is assigned ``
|
11
|
+
Then the value of the response body is empty
|
12
|
+
|
13
|
+
Scenario: Whitespace-only body is empty.
|
14
|
+
When the response body is assigned:
|
15
|
+
\"\"\"
|
16
|
+
|
17
|
+
\"\"\"
|
18
|
+
Then the value of the response body is empty
|
19
|
+
|
20
|
+
Scenario: Empty string is empty.
|
21
|
+
When the response body is assigned `""`
|
22
|
+
Then the value of the response body is empty
|
23
|
+
|
24
|
+
Scenario: Non-empty string is not empty.
|
25
|
+
When the response body is assigned `blah`
|
26
|
+
Then the value of the response body is not empty
|
27
|
+
|
28
|
+
Scenario: Quoted whitespace is not empty.
|
29
|
+
When the response body is assigned `" "`
|
30
|
+
Then the value of the response body is not empty
|
31
|
+
|
32
|
+
Scenario: Empty arrays are empty.
|
33
|
+
When the response body is assigned `[]`
|
34
|
+
Then the value of the response body is empty
|
35
|
+
|
36
|
+
Scenario: Non-empty arrays are not empty.
|
37
|
+
When the response body is assigned `[[]]`
|
38
|
+
Then the value of the response body is not empty
|
39
|
+
|
40
|
+
Scenario: Empty objects are empty.
|
41
|
+
When the response body is assigned `{}`
|
42
|
+
Then the value of the response body is empty
|
43
|
+
|
44
|
+
Scenario: Non-empty objects are not empty.
|
45
|
+
When the response body is assigned `{"foo":{}}`
|
46
|
+
Then the value of the response body is not empty
|
47
|
+
|
48
|
+
Scenario: Null values are empty.
|
49
|
+
When the response body is assigned `{"foo": null}`
|
50
|
+
Then the value of the response body child `foo` is empty
|
51
|
+
|
52
|
+
Scenario: False is not empty.
|
53
|
+
When the response body is assigned `false`
|
54
|
+
Then the value of the response body is not empty
|
55
|
+
|
56
|
+
Scenario: 0 is not empty.
|
57
|
+
When the response body is assigned `0`
|
58
|
+
Then the value of the response body is not empty
|
59
|
+
|
60
|
+
"""
|
61
|
+
When I run `cucumber --strict features/is_empty.feature`
|
62
|
+
Then the output should contain:
|
63
|
+
"""
|
64
|
+
12 passed
|
65
|
+
"""
|
66
|
+
And it should pass
|
67
|
+
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Feature: Equal to
|
2
|
-
It can be asserted that a value is equal to another value
|
2
|
+
It can be asserted that a value is equal to another value.
|
3
3
|
|
4
|
-
Scenario: Assorted positive and negative assertions.
|
4
|
+
Scenario: Assorted positive and negative assertions pass.
|
5
5
|
Given a file named "features/is_equal_to.feature" with:
|
6
6
|
"""
|
7
7
|
|
@@ -1,12 +1,12 @@
|
|
1
1
|
Feature: Matching
|
2
|
-
It can be asserted that a value matches another string or regex
|
2
|
+
It can be asserted that a value matches another string or regex.
|
3
3
|
|
4
4
|
Scenario: Assorted positive and negative assertions.
|
5
5
|
Given a file named "features/is_matching.feature" with:
|
6
6
|
"""
|
7
7
|
|
8
|
-
Feature: Assert value matchiness
|
9
|
-
Scenario: String in response body matched against a regex
|
8
|
+
Feature: Assert value matchiness.
|
9
|
+
Scenario: String in response body is matched against a regex.
|
10
10
|
When the response body is assigned:
|
11
11
|
\"\"\"
|
12
12
|
http://www.github.com?var=val
|
@@ -32,4 +32,4 @@ Feature: Assert value matchiness
|
|
32
32
|
"""
|
33
33
|
2 passed
|
34
34
|
"""
|
35
|
-
And it should pass
|
35
|
+
And it should pass
|
data/lib/brine/requester.rb
CHANGED
@@ -32,21 +32,32 @@ module ClientBuilding
|
|
32
32
|
self
|
33
33
|
end
|
34
34
|
|
35
|
+
# This is represented as list of functions so that it can be more easily customized for
|
36
|
+
# unexpected use cases.
|
37
|
+
# It should likely be broken up a bit more sensibly and more useful insertion commands added...
|
38
|
+
# but it's likely enough of a power feature and platform specific to leave pretty raw.
|
39
|
+
def connection_handlers
|
40
|
+
@connection_handlers ||= [
|
41
|
+
proc do |conn|
|
42
|
+
conn.request :json
|
43
|
+
if @oauth2
|
44
|
+
conn.request :oauth2, @oauth2.token, :token_type => @oauth2.token_type
|
45
|
+
end
|
46
|
+
end,
|
47
|
+
proc do |conn|
|
48
|
+
if @logging
|
49
|
+
conn.response :logger, nil, :bodies => (@logging.casecmp('DEBUG') == 0)
|
50
|
+
end
|
51
|
+
conn.response :json, :content_type => /\bjson$/
|
52
|
+
end,
|
53
|
+
proc{|conn| conn.adapter Faraday.default_adapter }
|
54
|
+
]
|
55
|
+
end
|
56
|
+
|
35
57
|
def client_for_host(host, logging: ENV['BRINE_LOG_HTTP'])
|
58
|
+
@logging = logging
|
36
59
|
Faraday.new(host) do |conn|
|
37
|
-
|
38
|
-
|
39
|
-
if @oauth2
|
40
|
-
conn.request :oauth2, @oauth2.token, :token_type => @oauth2.token_type
|
41
|
-
end
|
42
|
-
|
43
|
-
if logging
|
44
|
-
conn.response :logger, nil, :bodies => (logging.casecmp('DEBUG') == 0)
|
45
|
-
end
|
46
|
-
|
47
|
-
conn.response :json, :content_type => /\bjson$/
|
48
|
-
|
49
|
-
conn.adapter Faraday.default_adapter
|
60
|
+
connection_handlers.each{|h| h.call(conn) }
|
50
61
|
end
|
51
62
|
end
|
52
63
|
end
|
@@ -25,6 +25,13 @@ Then(/^it is less than or equal to `([^`]*)`$/) do |value|
|
|
25
25
|
selector.assert_that(value) {|v| be <= v}
|
26
26
|
end
|
27
27
|
|
28
|
+
# Be a little smarter than default.
|
29
|
+
Then(/^it is empty$/) do
|
30
|
+
selector.assert_that(nil) do
|
31
|
+
satisfy{|i| i.nil? || (i.respond_to?(:empty?) && i.empty?) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
28
35
|
Then(/^it is including `([^`]*)`$/) do |value|
|
29
36
|
selector.assert_that(value) {|v| include v }
|
30
37
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: brine-dsl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Whipple
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cucumber
|
@@ -220,6 +220,7 @@ files:
|
|
220
220
|
- features/argument_transforms/template.feature
|
221
221
|
- features/argument_transforms/whitespace.feature
|
222
222
|
- features/assertions/is_a_valid.feature
|
223
|
+
- features/assertions/is_empty.feature
|
223
224
|
- features/assertions/is_equal_to.feature
|
224
225
|
- features/assertions/is_including.feature
|
225
226
|
- features/assertions/is_matching.feature
|
@@ -290,6 +291,7 @@ test_files:
|
|
290
291
|
- features/argument_transforms/template.feature
|
291
292
|
- features/argument_transforms/whitespace.feature
|
292
293
|
- features/assertions/is_a_valid.feature
|
294
|
+
- features/assertions/is_empty.feature
|
293
295
|
- features/assertions/is_equal_to.feature
|
294
296
|
- features/assertions/is_including.feature
|
295
297
|
- features/assertions/is_matching.feature
|