webspicy 0.16.3 → 0.17.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/README.md +8 -3
- data/examples/restful/Gemfile.lock +14 -13
- data/examples/restful/webspicy/config.rb +1 -0
- data/examples/restful/webspicy/{todo/deleteTodo.yml → formaldef/todo/_one/delete.yml} +3 -0
- data/examples/restful/webspicy/{todo/getTodoSingleServiceFormat.yml → formaldef/todo/_one/get.simpler.yml} +0 -0
- data/examples/restful/webspicy/{todo/getTodo.yml → formaldef/todo/_one/get.yml} +0 -0
- data/examples/restful/webspicy/{todo/patchTodo.yml → formaldef/todo/_one/patch.yml} +0 -0
- data/examples/restful/webspicy/{todo/putTodo.yml → formaldef/todo/_one/put.yml} +0 -0
- data/examples/restful/webspicy/{todo/getTodos.yml → formaldef/todo/get.yml} +0 -0
- data/examples/restful/webspicy/{todo → formaldef/todo}/options.yml +0 -0
- data/examples/restful/webspicy/{todo/postCsv.yml → formaldef/todo/post.csv.yml} +0 -0
- data/examples/restful/webspicy/{todo/postFile.yml → formaldef/todo/post.file.yml} +0 -0
- data/examples/restful/webspicy/{todo/postTodos.yml → formaldef/todo/post.yml} +0 -0
- data/examples/restful/webspicy/{todo → formaldef/todo}/todos.csv +0 -0
- data/examples/restful/webspicy/support/todo_not_removed.rb +21 -0
- data/examples/restful/webspicy/support/todo_removed.rb +5 -3
- data/lib/webspicy.rb +1 -0
- data/lib/webspicy/checker.rb +5 -20
- data/lib/webspicy/configuration.rb +9 -0
- data/lib/webspicy/configuration/scope.rb +0 -8
- data/lib/webspicy/formaldoc.fio +2 -0
- data/lib/webspicy/rspec/checker.rb +2 -0
- data/lib/webspicy/rspec/checker/rspec_checker.rb +24 -0
- data/lib/webspicy/rspec/support/rspec_runnable.rb +27 -0
- data/lib/webspicy/rspec/tester.rb +4 -0
- data/lib/webspicy/{tester → rspec/tester}/rspec_asserter.rb +23 -10
- data/lib/webspicy/{tester → rspec/tester}/rspec_matchers.rb +10 -0
- data/lib/webspicy/rspec/tester/rspec_tester.rb +63 -0
- data/lib/webspicy/specification.rb +5 -7
- data/lib/webspicy/specification/errcondition.rb +16 -0
- data/lib/webspicy/specification/service.rb +27 -19
- data/lib/webspicy/specification/test_case.rb +3 -9
- data/lib/webspicy/support.rb +1 -0
- data/lib/webspicy/support/data_object.rb +25 -0
- data/lib/webspicy/tester.rb +4 -78
- data/lib/webspicy/tester/asserter.rb +9 -4
- data/lib/webspicy/tester/assertions.rb +8 -9
- data/lib/webspicy/tester/failure.rb +6 -0
- data/lib/webspicy/tester/invocation.rb +8 -156
- data/lib/webspicy/version.rb +2 -2
- data/spec/unit/configuration/scope/test_each_service.rb +2 -2
- data/spec/unit/configuration/scope/test_each_specification.rb +7 -7
- data/spec/unit/test_configuration.rb +1 -1
- data/spec/unit/tester/test_asserter.rb +198 -3
- data/spec/unit/tester/test_assertions.rb +8 -6
- metadata +27 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63c15addd49634a62ba637d18cfee736f405dc0e144ec688e2fda6861764a09c
|
4
|
+
data.tar.gz: 903d3a0ca2f20a5dbac1fafc4a1a89981736d527cb69668a06b41acb5ef04696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d54f59f003313e38dd37104e56038b6ede06c7339718498e6dda9730de97acdd0fae5499d45463ef790f78332eb0d337a3f068e3dd08bfec901edb8125a710c2
|
7
|
+
data.tar.gz: c43077a9023045856e726c0d2dbe39abc83abbd198df4a765758e6578664bcc6ec44ec7df75e164b436ae6a89cb6fe0ee89b4fc995afd3fe068bef2e5e484144
|
data/README.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# Webspicy
|
2
2
|
|
3
3
|
A specification and test framework for web services seen as black-box software
|
4
|
-
operations. Webspicy yields a better test coverage for a smaller testing effort
|
5
|
-
because software quality matters.
|
4
|
+
operations. Webspicy yields a better test coverage for a smaller testing effort.
|
6
5
|
|
7
6
|
See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
8
7
|
|
8
|
+
Have a look at `doc/*.md` for vocabulary and vision as well as `ROADMAP.md`.
|
9
|
+
|
9
10
|
## Features
|
10
11
|
|
11
12
|
* Declarative specification of HTTP web services + their tests
|
@@ -19,7 +20,7 @@ See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
|
19
20
|
your API design better.
|
20
21
|
|
21
22
|
* Formal and human-friendly data schema with strong data matching semantics,
|
22
|
-
thanks to finitio.io
|
23
|
+
thanks to [http://finitio.io](http://finitio.io)
|
23
24
|
|
24
25
|
* Test instrumentation and generation, based on PRE & POST contracts.
|
25
26
|
|
@@ -30,6 +31,10 @@ See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
|
30
31
|
* Extra goodies: when a specification is written, it can also be used for
|
31
32
|
mocking the API, generating an openapi file, etc.
|
32
33
|
|
34
|
+
## Is this used on real-world cases?
|
35
|
+
|
36
|
+
Yes, `webspicy` is currently used on a dozen production components. Our biggest specification has 324 specification files for thousands of tests, 35% of them being generated.
|
37
|
+
|
33
38
|
## Getting started with the commandline
|
34
39
|
|
35
40
|
To install webspicy on your developer computer, install ruby then:
|
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../..
|
3
3
|
specs:
|
4
|
-
webspicy (0.
|
4
|
+
webspicy (0.17.0)
|
5
5
|
colorize (~> 0.8.1)
|
6
6
|
finitio (~> 0.9.0)
|
7
7
|
http (>= 2)
|
@@ -11,7 +11,7 @@ PATH
|
|
11
11
|
path (~> 2.0)
|
12
12
|
rack-robustness (~> 1.1, >= 1.1.0)
|
13
13
|
rack-test (~> 0.6.3)
|
14
|
-
rspec (~> 3.
|
14
|
+
rspec (~> 3.10)
|
15
15
|
rspec_junit_formatter (~> 0.4.1)
|
16
16
|
|
17
17
|
GEM
|
@@ -27,11 +27,11 @@ GEM
|
|
27
27
|
diff-lcs (1.4.4)
|
28
28
|
domain_name (0.5.20190701)
|
29
29
|
unf (>= 0.0.5, < 1.0.0)
|
30
|
-
ffi (1.
|
30
|
+
ffi (1.14.2)
|
31
31
|
ffi-compiler (1.0.1)
|
32
32
|
ffi (>= 1.0.0)
|
33
33
|
rake
|
34
|
-
finitio (0.9.
|
34
|
+
finitio (0.9.1)
|
35
35
|
citrus (>= 2.4, < 4.0)
|
36
36
|
hansi (0.2.0)
|
37
37
|
http (4.4.1)
|
@@ -42,9 +42,9 @@ GEM
|
|
42
42
|
http-cookie (1.0.3)
|
43
43
|
domain_name (~> 0.5)
|
44
44
|
http-form_data (2.3.0)
|
45
|
-
http-parser (1.2.
|
46
|
-
ffi-compiler
|
47
|
-
i18n (1.8.
|
45
|
+
http-parser (1.2.3)
|
46
|
+
ffi-compiler (>= 1.0, < 2.0)
|
47
|
+
i18n (1.8.7)
|
48
48
|
concurrent-ruby (~> 1.0)
|
49
49
|
mustermann (1.1.1)
|
50
50
|
ruby2_keywords (~> 0.0.1)
|
@@ -55,7 +55,7 @@ GEM
|
|
55
55
|
commonmarker (~> 0.17)
|
56
56
|
psych (~> 3.1)
|
57
57
|
path (2.0.1)
|
58
|
-
psych (3.
|
58
|
+
psych (3.3.0)
|
59
59
|
public_suffix (4.0.6)
|
60
60
|
rack (2.2.3)
|
61
61
|
rack-protection (2.1.0)
|
@@ -68,15 +68,15 @@ GEM
|
|
68
68
|
rspec-core (~> 3.10.0)
|
69
69
|
rspec-expectations (~> 3.10.0)
|
70
70
|
rspec-mocks (~> 3.10.0)
|
71
|
-
rspec-core (3.10.
|
71
|
+
rspec-core (3.10.1)
|
72
72
|
rspec-support (~> 3.10.0)
|
73
|
-
rspec-expectations (3.10.
|
73
|
+
rspec-expectations (3.10.1)
|
74
74
|
diff-lcs (>= 1.2.0, < 2.0)
|
75
75
|
rspec-support (~> 3.10.0)
|
76
|
-
rspec-mocks (3.10.
|
76
|
+
rspec-mocks (3.10.1)
|
77
77
|
diff-lcs (>= 1.2.0, < 2.0)
|
78
78
|
rspec-support (~> 3.10.0)
|
79
|
-
rspec-support (3.10.
|
79
|
+
rspec-support (3.10.1)
|
80
80
|
rspec_junit_formatter (0.4.1)
|
81
81
|
rspec-core (>= 2, < 4, != 2.12.0)
|
82
82
|
ruby-enum (0.8.0)
|
@@ -95,6 +95,7 @@ GEM
|
|
95
95
|
PLATFORMS
|
96
96
|
ruby
|
97
97
|
x86_64-darwin-15
|
98
|
+
x86_64-darwin-18
|
98
99
|
|
99
100
|
DEPENDENCIES
|
100
101
|
rake (~> 12)
|
@@ -102,4 +103,4 @@ DEPENDENCIES
|
|
102
103
|
webspicy!
|
103
104
|
|
104
105
|
BUNDLED WITH
|
105
|
-
2.2.
|
106
|
+
2.2.2
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class TodoNotRemoved
|
2
|
+
include Webspicy::Specification::Postcondition
|
3
|
+
|
4
|
+
def self.match(service, descr)
|
5
|
+
return TodoNotRemoved.new if descr =~ /If it existed, the todo has not been removed/
|
6
|
+
end
|
7
|
+
|
8
|
+
def check(invocation)
|
9
|
+
client, scope, test_case = invocation.client,
|
10
|
+
invocation.client.scope,
|
11
|
+
invocation.test_case
|
12
|
+
return if invocation.response.status == 404
|
13
|
+
id = test_case.params['id']
|
14
|
+
url = scope.to_real_url("/todo/#{id}", test_case){|url| url }
|
15
|
+
response = client.api.get(url, {}, {
|
16
|
+
"Accept" => "application/json"
|
17
|
+
})
|
18
|
+
return nil if response.status == 200
|
19
|
+
"Todo `#{id}` was not supposed to be deleted, it was not found"
|
20
|
+
end
|
21
|
+
end
|
@@ -6,9 +6,11 @@ class TodoRemoved
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def check(invocation)
|
9
|
-
client = invocation.client
|
10
|
-
|
11
|
-
|
9
|
+
client, scope, test_case = invocation.client,
|
10
|
+
invocation.client.scope,
|
11
|
+
invocation.test_case
|
12
|
+
id = test_case.params['id']
|
13
|
+
url = scope.to_real_url("/todo/#{id}", test_case){|url| url }
|
12
14
|
response = client.api.get(url, {}, {
|
13
15
|
"Accept" => "application/json"
|
14
16
|
})
|
data/lib/webspicy.rb
CHANGED
data/lib/webspicy/checker.rb
CHANGED
@@ -1,25 +1,10 @@
|
|
1
1
|
module Webspicy
|
2
2
|
class Checker
|
3
3
|
|
4
|
-
def
|
5
|
-
|
4
|
+
def self.new(*args, &bl)
|
5
|
+
require_relative 'rspec/checker'
|
6
|
+
RSpecChecker.new(*args, &bl)
|
6
7
|
end
|
7
|
-
attr_reader :config
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
scope.each_specification_file do |file, folder|
|
12
|
-
RSpec.describe file.relative_to(folder).to_s do
|
13
|
-
|
14
|
-
it 'meets the formal doc data schema' do
|
15
|
-
Webspicy.specification(file.load, file, scope)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
RSpec::Core::Runner.run config.rspec_options
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
9
|
+
end # class Checker
|
10
|
+
end # module Webspicy
|
@@ -9,6 +9,7 @@ module Webspicy
|
|
9
9
|
@children = []
|
10
10
|
@preconditions = []
|
11
11
|
@postconditions = []
|
12
|
+
@errconditions = []
|
12
13
|
@listeners = Hash.new{|h,k| h[k] = [] }
|
13
14
|
@rspec_options = default_rspec_options
|
14
15
|
@run_examples = default_run_examples
|
@@ -126,6 +127,13 @@ module Webspicy
|
|
126
127
|
attr_accessor :postconditions
|
127
128
|
protected :postconditions=
|
128
129
|
|
130
|
+
# Registers an errcondition matcher
|
131
|
+
def errcondition(clazz)
|
132
|
+
errconditions << clazz
|
133
|
+
end
|
134
|
+
attr_accessor :errconditions
|
135
|
+
protected :errconditions=
|
136
|
+
|
129
137
|
# Returns whether this configuration has children configurations or not
|
130
138
|
def has_children?
|
131
139
|
!children.empty?
|
@@ -422,6 +430,7 @@ module Webspicy
|
|
422
430
|
d.children = []
|
423
431
|
d.preconditions = self.preconditions.dup
|
424
432
|
d.postconditions = self.postconditions.dup
|
433
|
+
d.errconditions = self.errconditions.dup
|
425
434
|
d.rspec_options = self.rspec_options.dup
|
426
435
|
d.listeners = LISTENER_KINDS.inject({}){|ls,kind|
|
427
436
|
ls.merge(kind => self.listeners(kind).dup)
|
data/lib/webspicy/formaldoc.fio
CHANGED
@@ -25,6 +25,7 @@ Specification = .Webspicy::Specification
|
|
25
25
|
description : String
|
26
26
|
preconditions :? [String]|String
|
27
27
|
postconditions :? [String]|String
|
28
|
+
errconditions :? [String]|String
|
28
29
|
input_schema : Schema
|
29
30
|
output_schema : Schema
|
30
31
|
error_schema : Schema
|
@@ -45,6 +46,7 @@ Service =
|
|
45
46
|
description : String
|
46
47
|
preconditions :? [String]|String
|
47
48
|
postconditions :? [String]|String
|
49
|
+
errconditions :? [String]|String
|
48
50
|
input_schema : Schema
|
49
51
|
output_schema : Schema
|
50
52
|
error_schema : Schema
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class Checker
|
3
|
+
class RSpecChecker
|
4
|
+
include Webspicy::Support::RSpecRunnable
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def load_rspec_examples
|
9
|
+
config.each_scope do |scope|
|
10
|
+
scope.each_specification_file do |file, folder|
|
11
|
+
RSpec.describe file.relative_to(folder).to_s do
|
12
|
+
|
13
|
+
it 'meets the formal doc data schema' do
|
14
|
+
Webspicy.specification(file.load, file, scope)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class RSpecChecker
|
23
|
+
end # class Checker
|
24
|
+
end # module Webspicy
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Support
|
3
|
+
module RSpecRunnable
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = Configuration.dress(config)
|
7
|
+
end
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def reset_rspec!
|
11
|
+
RSpec.reset
|
12
|
+
RSpec.configure do |c|
|
13
|
+
c.filter_gems_from_backtrace "rake"
|
14
|
+
end
|
15
|
+
load_rspec_examples
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(err=$stderr, out=$stdout)
|
19
|
+
reset_rspec!
|
20
|
+
options = RSpec::Core::ConfigurationOptions.new(config.rspec_options)
|
21
|
+
conf = RSpec::Core::Configuration.new
|
22
|
+
RSpec::Core::Runner.new(options, conf).run(err, out)
|
23
|
+
end
|
24
|
+
|
25
|
+
end # module RSpecRunnable
|
26
|
+
end # module Support
|
27
|
+
end # module Webspicy
|
@@ -8,6 +8,10 @@ module Webspicy
|
|
8
8
|
end
|
9
9
|
attr_reader :rspec, :invocation
|
10
10
|
|
11
|
+
def self.call(rspec, invocation)
|
12
|
+
new(rspec, invocation).send(:assert!)
|
13
|
+
end
|
14
|
+
|
11
15
|
def response
|
12
16
|
invocation.response
|
13
17
|
end
|
@@ -20,15 +24,20 @@ module Webspicy
|
|
20
24
|
test_case.service
|
21
25
|
end
|
22
26
|
|
27
|
+
protected
|
28
|
+
|
23
29
|
def assert!
|
24
|
-
|
25
|
-
|
26
|
-
|
30
|
+
rspec.aggregate_failures do
|
31
|
+
assert_status_met
|
32
|
+
assert_content_type_met
|
33
|
+
assert_expected_headers
|
34
|
+
end
|
27
35
|
assert_output_schema_met
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
36
|
+
rspec.aggregate_failures do
|
37
|
+
assert_assertions_met
|
38
|
+
assert_postconditions_met
|
39
|
+
assert_errconditions_met
|
40
|
+
end
|
32
41
|
end
|
33
42
|
|
34
43
|
def assert_status_met
|
@@ -98,9 +107,13 @@ module Webspicy
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
101
|
-
def
|
102
|
-
|
103
|
-
|
110
|
+
def assert_errconditions_met
|
111
|
+
return unless service.has_errconditions?
|
112
|
+
return unless test_case.counterexample?
|
113
|
+
service.errconditions.each do |post|
|
114
|
+
msg = post.check(invocation)
|
115
|
+
rspec.expect(msg).to rspec.meet_errcondition(post)
|
116
|
+
end
|
104
117
|
end
|
105
118
|
|
106
119
|
end # class RSpecAsserter
|
@@ -94,6 +94,16 @@ RSpec::Matchers.define :meet_postcondition do |post|
|
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
97
|
+
RSpec::Matchers.define :meet_errcondition do |post|
|
98
|
+
match do |actual|
|
99
|
+
actual.nil?
|
100
|
+
end
|
101
|
+
failure_message_for_should do |actual|
|
102
|
+
"expected errcondition `#{post.class.name}` to be met, got following error:\n" + \
|
103
|
+
" #{actual}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
97
107
|
RSpec::Matchers.define :be_an_empty_errors_array do
|
98
108
|
match do |actual|
|
99
109
|
actual.empty?
|