hyperspec 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,118 @@
1
+ # This is Work in Progress
2
+
3
+ __ __ ______
4
+ / / / /_ _________________/ ____/________________
5
+ / /_/ / / / / __ \ _ \_ __/____ \/ __ \ _ \ ___/
6
+ / __ / /_/ / /_/ / __/ / ____/ / /_/ / __/ /__
7
+ /_/ /_/\__, / ____/\___/_/ /_____/ ____/\___/\___/
8
+ /____/_/ /_/
9
+
10
+ A full stack testing framework for HTTP APIs.
11
+
12
+ By extending `minitest/spec` HyperSpec provides a Ruby DSL for testing
13
+ HTTP APIs from the outside.
14
+
15
+ ## Example
16
+
17
+ service "http://localhost:4567" do
18
+ def response_json
19
+ JSON.parse(response.body)
20
+ end
21
+
22
+ resource "/lolz" do
23
+ get do
24
+ it { responds_with.status :ok }
25
+ it { response_json['lolz'].must_be_instance_of Array }
26
+
27
+ with_query("q=monorail") do
28
+ it "only lists lolz that match the query" do
29
+ response_json['lolz'].wont_be_empty
30
+ response_json['lolz'].each do |lol|
31
+ lol['title'].must_match /monorail/
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ post do
38
+ describe "without request body" do
39
+ it { responds_with.status :unprocessable_entity }
40
+ end
41
+
42
+ describe "with request body" do
43
+ with_headers({ 'Content-Type' => 'application/json' }) do
44
+ with_request_body({ "title" => "Roflcopter!" }.to_json) do
45
+ it { responds_with.status :created }
46
+ it { response_json['lol']['title'].must_equal 'Roflcopter!' }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ ## Concepts
55
+
56
+ ### `service`
57
+
58
+ Sets the `BASE_URI` of the API.
59
+
60
+ ### `resource`
61
+
62
+ Sets the `URI` under test. Absolute or relative to the current scope.
63
+
64
+ ### `get`, `head`, `post`, `put` and `delete`
65
+
66
+ Selects the HTTP method for the request.
67
+
68
+ ### `with_query_string`
69
+
70
+ Sets the query parameters used for a request. Merges with previously set parameters.
71
+
72
+ ### `with_headers`
73
+
74
+ Sets the headers used for a request. Merges with previously set headers.
75
+
76
+ ### `with_body`
77
+
78
+ Sets the body used for a request. Overrides previously set parameters.
79
+
80
+ ### `response`
81
+
82
+ An object for accessing properties of the "raw" response.
83
+
84
+ ### `responds_with`
85
+
86
+ Issues the request and returns an object that has the following convenience matchers:
87
+
88
+ #### `status`
89
+
90
+ Allows for comparing against named status code symbols.
91
+
92
+ #### `content_type`
93
+
94
+ #### `content_charset`
95
+
96
+ ## Upcoming features
97
+
98
+ ### Representations
99
+
100
+ - DSL for matching representations.
101
+
102
+ ## Concerns
103
+
104
+ - Efficient ways of building up and verifying precondition state.
105
+ - Verify an eventual consistent state.
106
+ - Allowing whitebox testing by "wormholing" into the application(s).
107
+
108
+ ## Acknowledgments
109
+
110
+ Thanks to Daniel Bornkessel for inspiring me to do README driven development.
111
+
112
+ ## History
113
+
114
+ ### 2012 02 07
115
+
116
+ #### 0.0.1
117
+
118
+ - Initial release
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ require 'sinatra'
2
+ require 'json'
3
+
4
+ get "/lolz" do
5
+ title = params[:q]
6
+ %^{"lolz": [ { "title": "#{title}" } ]}^
7
+ end
8
+
9
+ post "/lolz" do
10
+ if request.env['CONTENT_TYPE'] == "application/json"
11
+ params = JSON.parse(request.env['rack.input'].read)
12
+
13
+ if params.nil? || params.empty?
14
+ status 422
15
+ %^{"error": "An error occurred!"}^
16
+ else
17
+ status 201
18
+ %^{"lol": {"title": "Roflcopter!"}}^
19
+ end
20
+ else
21
+ status 422
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+
3
+ require './example'
4
+
5
+ run Sinatra::Application
@@ -0,0 +1,52 @@
1
+ require 'hyperspec'
2
+ require 'minitest/autorun'
3
+
4
+ require 'json'
5
+
6
+ # Run `shotgun -p 4567 example.ru` from `examples/service/`.
7
+
8
+ service "http://localhost:4567" do
9
+ def response_json
10
+ JSON.parse(response.body)
11
+ end
12
+
13
+ resource "/lolz" do
14
+ get do
15
+ it { responds_with.status :ok }
16
+ it { response_json['lolz'].must_be_instance_of Array }
17
+
18
+ with_query("q=monorail") do
19
+ it "only lists lolz that match the query" do
20
+ response_json['lolz'].wont_be_empty
21
+ response_json['lolz'].each do |lol|
22
+ lol['title'].must_match /monorail/
23
+ end
24
+ end
25
+ end
26
+
27
+ with_query("q=looong") do
28
+ it "only lists lolz that match the query" do
29
+ response_json['lolz'].wont_be_empty
30
+ response_json['lolz'].each do |lol|
31
+ lol['title'].must_match /looong/
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ post do
38
+ describe "without request body" do
39
+ it { responds_with.status :unprocessable_entity }
40
+ end
41
+
42
+ describe "with request body" do
43
+ with_headers({ 'Content-Type' => 'application/json' }) do
44
+ with_request_body({ "title" => "Roflcopter!" }.to_json) do
45
+ it { responds_with.status :created }
46
+ it { response_json['lol']['title'].must_equal 'Roflcopter!' }
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "hyperspec/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "hyperspec"
7
+ s.version = HyperSpec::VERSION
8
+ s.authors = [ "Hannes Tydén" ]
9
+ s.email = [ "hannes@soundcloud.com" ]
10
+ s.homepage = "http://github.com/hannestyden/hyperspec"
11
+ s.summary = "Full stack HTTP API testing DSL."
12
+ s.description = <<-DESCRIPTION
13
+ By extending minitest/spec HyperSpec provides a Ruby DSL for testing HTTP APIs from the "outside".
14
+ DESCRIPTION
15
+
16
+ # Required for validation.
17
+ s.rubyforge_project = "hyperspec"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ s.require_paths = [ "lib" ]
23
+
24
+ dependencies =
25
+ [
26
+ [ "minitest", "~> 2.11" ],
27
+ ]
28
+
29
+ developement_dependencies =
30
+ [
31
+ [ "vcr", "~> 1.6" ],
32
+ [ "webmock" ],
33
+ ]
34
+
35
+ runtime_dependencies = []
36
+
37
+ (dependencies + developement_dependencies).each do |dependency|
38
+ s.add_development_dependency *dependency
39
+ end
40
+
41
+ (dependencies + developement_dependencies).each do |dependency|
42
+ s.add_runtime_dependency *dependency
43
+ end
44
+ end
@@ -0,0 +1,193 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+
4
+ gem 'minitest' # Ensure gem is used over built in.
5
+ require 'minitest/spec'
6
+
7
+ require 'hyperspec/version'
8
+
9
+ module HyperSpec
10
+ module ObjectExtensions
11
+ private
12
+ def service(desc, additional_desc = nil, &block)
13
+ cls = describe(desc, additional_desc, &block)
14
+ cls.send(:define_method, :base_uri) { URI.parse(desc) }
15
+ cls.send(:define_method, :headers) { {} }
16
+ cls.send(:define_method, :request_body) { "" }
17
+ cls
18
+ end
19
+
20
+ def resource(path, additional_desc = nil, &block)
21
+ cls = describe(desc, additional_desc, &block)
22
+
23
+ cls.send(:define_method, :base_uri) do
24
+ s = super()
25
+ s.path = [ s.path, path ].reject(&:empty?).join("/")
26
+ s
27
+ end
28
+ cls
29
+ end
30
+
31
+ def with_headers(hash, additional_desc = nil, &block)
32
+ cls = describe("with headers", additional_desc, &block)
33
+ cls.send(:define_method, :headers) { super().merge(hash) }
34
+ cls
35
+ end
36
+
37
+ def with_query(string, additional_desc = nil, &block)
38
+ cls = describe("with query", additional_desc, &block)
39
+ cls.send(:define_method, :base_uri) do
40
+ s = super()
41
+ s.query = [ s.query.to_s, string ].reject(&:empty?).join("&")
42
+ s
43
+ end
44
+ cls
45
+ end
46
+
47
+ def with_request_body(string, additional_desc = nil, &block)
48
+ cls = describe("with request body", additional_desc, &block)
49
+ cls.send(:define_method, :request_body) do
50
+ string
51
+ end
52
+ cls
53
+ end
54
+
55
+ # HTTP method selection
56
+ #
57
+ def get(additional_desc = nil, &block)
58
+ cls = describe('GET', additional_desc, &block)
59
+ cls.instance_eval { |_| def request_type; :get; end }
60
+ cls
61
+ end
62
+
63
+ def head(additional_desc = nil, &block)
64
+ cls = describe('HEAD', additional_desc, &block)
65
+ cls.instance_eval { |_| def request_type; :head; end }
66
+ cls
67
+ end
68
+ def post(additional_desc = nil, &block)
69
+ cls = describe('POST', additional_desc, &block)
70
+ cls.instance_eval { |_| def request_type; :post; end }
71
+ cls
72
+ end
73
+ def put(additional_desc = nil, &block)
74
+ cls = describe('PUT', additional_desc, &block)
75
+ cls.instance_eval { |_| def request_type; :put; end }
76
+ cls
77
+ end
78
+ def delete(additional_desc = nil, &block)
79
+ cls = describe('DELETE', additional_desc, &block)
80
+ cls.instance_eval { |_| def request_type; :delete; end }
81
+ cls
82
+ end
83
+ end
84
+
85
+ module MiniTest
86
+ module SpecExtensions
87
+ def response
88
+ do_request
89
+ end
90
+
91
+ def request_type
92
+ self.class.request_type
93
+ end
94
+
95
+ def responds_with
96
+ Have.new(response)
97
+ end
98
+
99
+ private
100
+ def do_request
101
+ klass = eval("Net::HTTP::#{request_type.to_s.gsub(/^\w/) { |c| c.upcase }}")
102
+ @do_request ||=
103
+ request_response(klass, base_uri, headers, request_body)
104
+ end
105
+
106
+ def request_response(klass, uri, headers, body = '')
107
+ http = Net::HTTP.new(uri.host, uri.port)
108
+ http.use_ssl = URI::HTTPS === uri
109
+
110
+ # NO DON'T DO IT!
111
+ # http.verify_mode = OpenSSL::SSL::VERIFY_NONE
112
+ # YOU DIDN'T DO IT, DID YOU?
113
+
114
+ resp =
115
+ http.start do
116
+ request_uri = [ uri.path, uri.query ].join("?")
117
+ req = klass.new(request_uri)
118
+ headers.inject(req) { |m, (k, v)| m[k] = v; m }
119
+ req.body = body if body
120
+ if headers['Content-Type']
121
+ req.content_type = headers['Content-Type']
122
+ end
123
+ http.request(req)
124
+ end
125
+ Response.from_net_http_response(resp)
126
+ end
127
+ end
128
+ end
129
+
130
+ Response = Struct.new(:status_code, :headers, :body) do
131
+ STATI = {
132
+ # 2xx
133
+ 200 => :ok,
134
+ 201 => :created,
135
+
136
+ # 4xx
137
+ 401 => :unauthorized,
138
+ 411 => :length_required,
139
+
140
+ # WebDav extensions
141
+ 422 => :unprocessable_entity,
142
+ }
143
+
144
+ def self.from_net_http_response(http)
145
+ status_code = http.code.to_i
146
+ body = http.body
147
+
148
+ headers =
149
+ CaseInsensitiveHash.from_hash(http.to_hash)
150
+
151
+ new(status_code, headers, body)
152
+ end
153
+
154
+ def content_type
155
+ headers['Content-Type'].split(';').first
156
+ end
157
+
158
+ def content_charset
159
+ (md = headers['Content-Type'].match(/;charset=(.*)/)) && md[1]
160
+ end
161
+
162
+ def status
163
+ STATI[status_code]
164
+ end
165
+ end
166
+
167
+ class CaseInsensitiveHash < Hash
168
+ def self.from_hash(hash)
169
+ hash.inject(new) do |m, (k, v)|
170
+ m[k] = v.first
171
+ m
172
+ end
173
+ end
174
+
175
+ def []=(key, value)
176
+ super(key.downcase, value)
177
+ end
178
+
179
+ def [](key)
180
+ super(key.downcase)
181
+ end
182
+ end
183
+
184
+ Have = Struct.new(:proxy) do
185
+ def method_missing(method_name, *arguments, &block)
186
+ proxy.send(method_name).must_equal(*arguments)
187
+ end
188
+ end
189
+ end
190
+
191
+
192
+ ::Object.send(:include, HyperSpec::ObjectExtensions)
193
+ ::MiniTest::Spec.send(:include, HyperSpec::MiniTest::SpecExtensions)
@@ -0,0 +1,6 @@
1
+ module HyperSpec
2
+ MAJOR = 0
3
+ MINOR = 0
4
+ PATCH = 1
5
+ VERSION = [ MAJOR, MINOR, PATCH ].join(".")
6
+ end
@@ -0,0 +1,94 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :get
5
+ uri: http://localhost:80/
6
+ headers:
7
+ body:
8
+ response: !ruby/struct:VCR::Response
9
+ http_version: "1.1"
10
+ status: !ruby/struct:VCR::ResponseStatus
11
+ code: 200
12
+ message: OK
13
+ headers:
14
+ content-length:
15
+ - "22"
16
+ content-type:
17
+ - application/json;charset=utf-8
18
+ body: |+
19
+ {"is-this-json":true}
20
+
21
+ - !ruby/struct:VCR::HTTPInteraction
22
+ request: !ruby/struct:VCR::Request
23
+ method: :head
24
+ uri: http://localhost:80/
25
+ headers:
26
+ body:
27
+ response: !ruby/struct:VCR::Response
28
+ http_version: "1.1"
29
+ status: !ruby/struct:VCR::ResponseStatus
30
+ code: 200
31
+ message: OK
32
+ headers:
33
+ content-length:
34
+ - "22"
35
+ content-type:
36
+ - application/json;charset=utf-8
37
+ body:
38
+ - !ruby/struct:VCR::HTTPInteraction
39
+ request: !ruby/struct:VCR::Request
40
+ method: :post
41
+ uri: http://localhost:80/
42
+ headers:
43
+ body:
44
+ response: !ruby/struct:VCR::Response
45
+ http_version: "1.1"
46
+ status: !ruby/struct:VCR::ResponseStatus
47
+ code: 200
48
+ message: OK
49
+ headers:
50
+ content-length:
51
+ - "22"
52
+ content-type:
53
+ - application/json;charset=utf-8
54
+ body: |+
55
+ {"is-this-json":true}
56
+
57
+ - !ruby/struct:VCR::HTTPInteraction
58
+ request: !ruby/struct:VCR::Request
59
+ method: :put
60
+ uri: http://localhost:80/
61
+ headers:
62
+ body:
63
+ response: !ruby/struct:VCR::Response
64
+ http_version: "1.1"
65
+ status: !ruby/struct:VCR::ResponseStatus
66
+ code: 200
67
+ message: OK
68
+ headers:
69
+ content-length:
70
+ - "22"
71
+ content-type:
72
+ - application/json;charset=utf-8
73
+ body: |+
74
+ {"is-this-json":true}
75
+
76
+ - !ruby/struct:VCR::HTTPInteraction
77
+ request: !ruby/struct:VCR::Request
78
+ method: :delete
79
+ uri: http://localhost:80/
80
+ headers:
81
+ body:
82
+ response: !ruby/struct:VCR::Response
83
+ http_version: "1.1"
84
+ status: !ruby/struct:VCR::ResponseStatus
85
+ code: 200
86
+ message: OK
87
+ headers:
88
+ content-length:
89
+ - "22"
90
+ content-type:
91
+ - application/json;charset=utf-8
92
+ body: |+
93
+ {"is-this-json":true}
94
+
@@ -0,0 +1,202 @@
1
+ require 'uri'
2
+
3
+ gem 'minitest' # Ensure gem is used over built in.
4
+ require 'minitest/spec'
5
+ require 'minitest/autorun'
6
+
7
+ require 'vcr'
8
+ require './spec/support/vcr'
9
+ require './spec/support/meta_spec'
10
+
11
+ $:.push File.expand_path("../../lib", __FILE__)
12
+ require 'hyperspec'
13
+
14
+ VCR.config do |c|
15
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
16
+ c.stub_with :fakeweb
17
+ end
18
+
19
+ describe HyperSpec do
20
+ include MetaSpec
21
+
22
+ use_vcr_cassette('localhost')
23
+
24
+ it "should be of version 0.0.0" do
25
+ HyperSpec::VERSION.must_equal "0.0.0"
26
+ end
27
+
28
+ describe "MiniTest::Spec extensions" do
29
+ describe "service" do
30
+ # `service` is added to `Kernel`, but conveniently enough
31
+ # returns a sub class of MiniTest::Spec which can be tested.
32
+ subject do
33
+ service("http://lolc.at") {}.
34
+ new("Required name.")
35
+ end
36
+
37
+ it { subject.must_be_kind_of MiniTest::Spec }
38
+ it { subject.base_uri.must_equal URI.parse("http://lolc.at/") }
39
+ it { subject.headers.must_equal({}) }
40
+ end
41
+
42
+ describe "resource" do
43
+ # `resource` is added to `Kernel`, but conveniently enough
44
+ # returns a sub class of MiniTest::Spec which can be tested,
45
+ # though this time we must tap into the `service` and bind it.
46
+ subject do
47
+ the_spec do |bound|
48
+ service("http://lolc.at") do
49
+ bound.value = resource("/lolz") {}
50
+ end
51
+ end
52
+ end
53
+
54
+ it { subject.must_be_kind_of MiniTest::Spec }
55
+ it { subject.base_uri.must_equal URI.parse("http://lolc.at/lolz") }
56
+ it { subject.headers.must_equal({}) }
57
+ end
58
+
59
+ describe "nested resource" do
60
+ # `resource` is added to `Kernel`, but conveniently enough
61
+ # returns a sub class of MiniTest::Spec which can be tested,
62
+ # though this time we must tap into the `service` and bind it.
63
+ subject do
64
+ the_spec do |bound|
65
+ service("http://lolc.at") do
66
+ resource("/lolz") do
67
+ bound.value = resource("catz") {}
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ it { subject.base_uri.must_equal URI.parse("http://lolc.at/lolz/catz") }
74
+ end
75
+
76
+ describe "with_headers" do
77
+ subject do
78
+ the_spec do |bound|
79
+ service("http://localhost") do
80
+ resource("/lolz") do
81
+ bound.value = with_headers({ 'X-Camel-Size' => 'LARGE' }) {}
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ it { subject.must_be_kind_of MiniTest::Spec }
88
+ it { subject.base_uri.must_equal URI.parse("http://localhost/lolz") }
89
+ it { subject.headers.must_equal({ 'X-Camel-Size' => 'LARGE' }) }
90
+ end
91
+
92
+ describe "with_query" do
93
+ subject do
94
+ the_spec do |bound|
95
+ service("http://localhost") do
96
+ resource("/lolz") do
97
+ bound.value = with_query("q=monorail") {}
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ it { subject.must_be_kind_of MiniTest::Spec }
104
+ it { subject.base_uri.query.must_equal "q=monorail" }
105
+ end
106
+
107
+ describe "with_request_body" do
108
+ subject do
109
+ the_spec do |bound|
110
+ service("http://localhost") do
111
+ resource("/lolz") do
112
+ bound.value = with_request_body("lol[title]=Roflcopter") {}
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ it { subject.must_be_kind_of MiniTest::Spec }
119
+ it { subject.request_body.must_equal "lol[title]=Roflcopter" }
120
+ end
121
+
122
+ %w[ get head post put delete ].map(&:to_sym).each do |http_method|
123
+ describe "HTTP method selection" do
124
+ subject do
125
+ the_spec do |bound|
126
+ service("http://localhost") do
127
+ resource("/") do
128
+ bound.value = send(http_method) {}
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ it { subject.request_type.must_equal http_method }
135
+ end
136
+ end
137
+
138
+ describe "response" do
139
+ subject do
140
+ the_spec do |bound|
141
+ service("http://localhost") do
142
+ resource("/") do
143
+ bound.value = get {}
144
+ end
145
+ end
146
+ end.response
147
+ end
148
+
149
+ it { subject.status_code.must_be_kind_of Integer }
150
+ it { subject.headers.must_be_kind_of Hash }
151
+ it { subject.body.must_be_kind_of String }
152
+
153
+ it { subject.content_type.must_be_kind_of String }
154
+ it { subject.status.must_be_kind_of Symbol }
155
+
156
+ describe "status" do
157
+ {
158
+ 200 => :ok,
159
+ 201 => :created,
160
+ 401 => :unauthorized,
161
+ 411 => :length_required,
162
+ 422 => :unprocessable_entity,
163
+ }.each do |code, status|
164
+ it do
165
+ subject.status_code = code
166
+ subject.status.must_equal status
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "content_charset" do
172
+ it { subject.content_charset.must_be_kind_of String }
173
+ it do
174
+ subject.headers['Content-Type'] = "application/xml"
175
+ subject.content_charset.must_be_nil
176
+ end
177
+ end
178
+
179
+ describe "header access" do
180
+ it "must be case insensitive" do
181
+ subject.headers['Content-Length'].must_equal \
182
+ subject.headers['content-length']
183
+ end
184
+ end
185
+ end
186
+
187
+ describe "responds_with" do
188
+ subject do
189
+ the_spec do |bound|
190
+ service("http://localhost") do
191
+ resource("/") do
192
+ bound.value = get {}
193
+ end
194
+ end
195
+ end.responds_with
196
+ end
197
+
198
+ it { subject.status_code 200 }
199
+ it { subject.status :ok }
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,14 @@
1
+ module MetaSpec
2
+ def the_spec(&block)
3
+ intercept(&block).new("The required name.")
4
+ end
5
+
6
+ private
7
+ Bound = Struct.new(:value)
8
+
9
+ def intercept(&block)
10
+ bound = Bound.new
11
+ yield bound
12
+ bound.value
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ ::MiniTest::Spec.send(:extend, VCR::RSpec::Macros)
metadata ADDED
@@ -0,0 +1,136 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyperspec
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - "Hannes Tyd\xC3\xA9n"
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2012-02-07 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: minitest
17
+ prerelease: false
18
+ requirement: &id001 !ruby/object:Gem::Requirement
19
+ none: false
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: "2.11"
24
+ type: :development
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: vcr
28
+ prerelease: false
29
+ requirement: &id002 !ruby/object:Gem::Requirement
30
+ none: false
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: "1.6"
35
+ type: :development
36
+ version_requirements: *id002
37
+ - !ruby/object:Gem::Dependency
38
+ name: webmock
39
+ prerelease: false
40
+ requirement: &id003 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id003
48
+ - !ruby/object:Gem::Dependency
49
+ name: minitest
50
+ prerelease: false
51
+ requirement: &id004 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ~>
55
+ - !ruby/object:Gem::Version
56
+ version: "2.11"
57
+ type: :runtime
58
+ version_requirements: *id004
59
+ - !ruby/object:Gem::Dependency
60
+ name: vcr
61
+ prerelease: false
62
+ requirement: &id005 !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: "1.6"
68
+ type: :runtime
69
+ version_requirements: *id005
70
+ - !ruby/object:Gem::Dependency
71
+ name: webmock
72
+ prerelease: false
73
+ requirement: &id006 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: "0"
79
+ type: :runtime
80
+ version_requirements: *id006
81
+ description: " By extending minitest/spec HyperSpec provides a Ruby DSL for testing HTTP APIs from the \"outside\".\n"
82
+ email:
83
+ - hannes@soundcloud.com
84
+ executables: []
85
+
86
+ extensions: []
87
+
88
+ extra_rdoc_files: []
89
+
90
+ files:
91
+ - .gitignore
92
+ - Gemfile
93
+ - README.md
94
+ - Rakefile
95
+ - examples/service/example.rb
96
+ - examples/service/example.ru
97
+ - examples/spec/example_spec.rb
98
+ - hyperspec.gemspec
99
+ - lib/hyperspec.rb
100
+ - lib/hyperspec/version.rb
101
+ - spec/fixtures/vcr_cassettes/localhost.yml
102
+ - spec/hyperspec_spec.rb
103
+ - spec/support/meta_spec.rb
104
+ - spec/support/vcr.rb
105
+ homepage: http://github.com/hannestyden/hyperspec
106
+ licenses: []
107
+
108
+ post_install_message:
109
+ rdoc_options: []
110
+
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: "0"
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project: hyperspec
128
+ rubygems_version: 1.8.15
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Full stack HTTP API testing DSL.
132
+ test_files:
133
+ - spec/fixtures/vcr_cassettes/localhost.yml
134
+ - spec/hyperspec_spec.rb
135
+ - spec/support/meta_spec.rb
136
+ - spec/support/vcr.rb