kookaburra 0.27.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -4
- data/Gemfile.lock +22 -14
- data/README.markdown +43 -27
- data/Rakefile +19 -2
- data/VERSION +1 -1
- data/kookaburra.gemspec +17 -16
- data/lib/kookaburra/api_driver.rb +178 -64
- data/lib/kookaburra/given_driver.rb +1 -1
- data/spec/integration/test_a_rack_application_spec.rb +22 -5
- data/spec/kookaburra/api_driver_spec.rb +79 -136
- metadata +29 -15
- data/lib/kookaburra/json_api_driver.rb +0 -65
- data/spec/kookaburra/json_api_driver_spec.rb +0 -95
data/Gemfile
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
source 'http://rubygems.org'
|
2
2
|
|
3
|
-
gem '
|
4
|
-
gem 'json_pure'
|
3
|
+
gem 'rest-client'
|
5
4
|
|
6
5
|
# Add dependencies to develop your gem here.
|
7
6
|
# Include everything needed to run rake, tests, features, etc.
|
8
7
|
group :development do
|
8
|
+
gem 'jruby-openssl', :platforms => :jruby
|
9
9
|
gem 'rspec'
|
10
10
|
gem 'capybara'
|
11
11
|
gem 'yard'
|
12
|
-
gem '
|
12
|
+
gem 'kramdown' # used by yard to format documentation, but not a dependency as far as the yard gem is concerned
|
13
13
|
gem 'jeweler'
|
14
14
|
gem 'reek'
|
15
15
|
gem 'sinatra'
|
16
|
-
gem 'find_a_port'
|
16
|
+
gem 'find_a_port'
|
17
|
+
gem 'json'
|
17
18
|
end
|
data/Gemfile.lock
CHANGED
@@ -2,6 +2,7 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
addressable (2.3.2)
|
5
|
+
bouncy-castle-java (1.5.0146.1)
|
5
6
|
capybara (1.1.2)
|
6
7
|
mime-types (>= 1.16)
|
7
8
|
nokogiri (>= 1.3.3)
|
@@ -9,10 +10,11 @@ GEM
|
|
9
10
|
rack-test (>= 0.5.4)
|
10
11
|
selenium-webdriver (~> 2.0)
|
11
12
|
xpath (~> 0.1.4)
|
12
|
-
childprocess (0.3.
|
13
|
+
childprocess (0.3.5)
|
13
14
|
ffi (~> 1.0, >= 1.0.6)
|
14
15
|
diff-lcs (1.1.3)
|
15
|
-
ffi (1.1.
|
16
|
+
ffi (1.1.5)
|
17
|
+
ffi (1.1.5-java)
|
16
18
|
find_a_port (1.0.1)
|
17
19
|
git (1.2.5)
|
18
20
|
jeweler (1.8.4)
|
@@ -20,28 +22,32 @@ GEM
|
|
20
22
|
git (>= 1.2.5)
|
21
23
|
rake
|
22
24
|
rdoc
|
23
|
-
|
24
|
-
|
25
|
+
jruby-openssl (0.7.7)
|
26
|
+
bouncy-castle-java (>= 1.5.0146.1)
|
27
|
+
json (1.7.5)
|
28
|
+
json (1.7.5-java)
|
29
|
+
kramdown (0.14.0)
|
25
30
|
libwebsocket (0.1.5)
|
26
31
|
addressable
|
27
32
|
mime-types (1.19)
|
28
33
|
multi_json (1.3.6)
|
29
34
|
nokogiri (1.5.5)
|
30
|
-
|
35
|
+
nokogiri (1.5.5-java)
|
31
36
|
rack (1.4.1)
|
32
37
|
rack-protection (1.2.0)
|
33
38
|
rack
|
34
|
-
rack-test (0.6.
|
39
|
+
rack-test (0.6.2)
|
35
40
|
rack (>= 1.0)
|
36
41
|
rake (0.9.2.2)
|
37
42
|
rdoc (3.12)
|
38
43
|
json (~> 1.4)
|
39
|
-
redcarpet (1.17.2)
|
40
44
|
reek (1.2.12)
|
41
45
|
ripper_ruby_parser (~> 0.0.7)
|
42
46
|
ruby2ruby (~> 1.2.5)
|
43
47
|
ruby_parser (~> 2.0)
|
44
48
|
sexp_processor (~> 3.0)
|
49
|
+
rest-client (1.6.7)
|
50
|
+
mime-types (>= 1.16)
|
45
51
|
ripper_ruby_parser (0.0.8)
|
46
52
|
sexp_processor (~> 3.0)
|
47
53
|
rspec (2.11.0)
|
@@ -49,9 +55,9 @@ GEM
|
|
49
55
|
rspec-expectations (~> 2.11.0)
|
50
56
|
rspec-mocks (~> 2.11.0)
|
51
57
|
rspec-core (2.11.1)
|
52
|
-
rspec-expectations (2.11.
|
58
|
+
rspec-expectations (2.11.3)
|
53
59
|
diff-lcs (~> 1.1.3)
|
54
|
-
rspec-mocks (2.11.
|
60
|
+
rspec-mocks (2.11.3)
|
55
61
|
ruby2ruby (1.2.5)
|
56
62
|
ruby_parser (~> 2.0)
|
57
63
|
sexp_processor (~> 3.0)
|
@@ -64,7 +70,7 @@ GEM
|
|
64
70
|
multi_json (~> 1.0)
|
65
71
|
rubyzip
|
66
72
|
sexp_processor (3.2.0)
|
67
|
-
sinatra (1.3.
|
73
|
+
sinatra (1.3.3)
|
68
74
|
rack (~> 1.3, >= 1.3.6)
|
69
75
|
rack-protection (~> 1.2)
|
70
76
|
tilt (~> 1.3, >= 1.3.3)
|
@@ -74,16 +80,18 @@ GEM
|
|
74
80
|
yard (0.8.2.1)
|
75
81
|
|
76
82
|
PLATFORMS
|
83
|
+
java
|
77
84
|
ruby
|
78
85
|
|
79
86
|
DEPENDENCIES
|
80
87
|
capybara
|
81
|
-
find_a_port
|
88
|
+
find_a_port
|
82
89
|
jeweler
|
83
|
-
|
84
|
-
|
85
|
-
|
90
|
+
jruby-openssl
|
91
|
+
json
|
92
|
+
kramdown
|
86
93
|
reek
|
94
|
+
rest-client
|
87
95
|
rspec
|
88
96
|
sinatra
|
89
97
|
yard
|
data/README.markdown
CHANGED
@@ -3,6 +3,11 @@
|
|
3
3
|
Kookaburra is a framework for implementing the [Window Driver] [Window Driver] pattern in
|
4
4
|
order to keep acceptance tests maintainable.
|
5
5
|
|
6
|
+
## Requirements ##
|
7
|
+
|
8
|
+
Requires Ruby 1.9. Tested with both MRI and JRUBY (not that you must run
|
9
|
+
JRuby in 1.9 compatability mode.)
|
10
|
+
|
6
11
|
## Installation ##
|
7
12
|
|
8
13
|
Kookaburra is available as a Rubygem and [published on Rubygems.org] [Kookaburra Gem],
|
@@ -111,13 +116,14 @@ and shut down a Rack application server. Just add the following to
|
|
111
116
|
|
112
117
|
require 'kookaburra/test_helpers'
|
113
118
|
require 'thwait'
|
119
|
+
require 'find_a_port' # from the find_a_port gem
|
114
120
|
|
115
121
|
# Change these to the files that define your custom GivenDriver and UIDriver
|
116
122
|
# implementations.
|
117
123
|
require 'my_app/kookaburra/given_driver'
|
118
124
|
require 'my_app/kookaburra/ui_driver'
|
119
125
|
|
120
|
-
APP_PORT =
|
126
|
+
APP_PORT = FindAPort.available_port
|
121
127
|
|
122
128
|
# c.app_host below should be set to whatever the root URL of your running
|
123
129
|
# application is.
|
@@ -141,6 +147,10 @@ and shut down a Rack application server. Just add the following to
|
|
141
147
|
c.before(:all, :type => :request) do
|
142
148
|
# Run the server process in a forked process, and get a handle on that
|
143
149
|
# process, so that it can be shut down after the tests run.
|
150
|
+
#
|
151
|
+
# Note that you cannot fork under JRuby and will need to use a thread.
|
152
|
+
# See `spec/integration/test_a_rack_application_spec.rb` for an
|
153
|
+
# example.
|
144
154
|
@rack_server_pid = fork do
|
145
155
|
Capybara.server_port = APP_PORT
|
146
156
|
Capybara::Server.new(MyApplication).boot
|
@@ -205,13 +215,14 @@ and shut down a Rack application server. Just add the following to
|
|
205
215
|
|
206
216
|
require 'kookaburra/test_helpers'
|
207
217
|
require 'thwait'
|
218
|
+
require 'find_a_port' # from the find_a_port gem
|
208
219
|
|
209
220
|
# Change these to the files that define your custom GivenDriver and UIDriver
|
210
221
|
# implementations.
|
211
222
|
require 'my_app/kookaburra/given_driver'
|
212
223
|
require 'my_app/kookaburra/ui_driver'
|
213
224
|
|
214
|
-
APP_PORT =
|
225
|
+
APP_PORT = FindAPort.available_port
|
215
226
|
|
216
227
|
# c.app_host below should be set to whatever the root URL of your running
|
217
228
|
# application is.
|
@@ -227,12 +238,16 @@ and shut down a Rack application server. Just add the following to
|
|
227
238
|
|
228
239
|
World(Kookaburra::TestHelpers)
|
229
240
|
|
230
|
-
# Start the application server prior to running
|
231
|
-
#
|
241
|
+
# Start the application server prior to running the tests.
|
242
|
+
# `MyApplication` below should be replaced with the object that
|
232
243
|
# implements the Rack `#call` interface for your application. For a Rails
|
233
244
|
# app, this would be along the lines of `MyAppName::Application`.
|
234
245
|
# Runs the server process in a forked process, and get a handle on that
|
235
246
|
# process, so that it can be shut down after the tests run.
|
247
|
+
#
|
248
|
+
# Note that you cannot fork under JRuby and will need to use a thread.
|
249
|
+
# See `spec/integration/test_a_rack_application_spec.rb` for an
|
250
|
+
# example.
|
236
251
|
@rack_server_pid = fork do
|
237
252
|
Capybara.server_port = APP_PORT
|
238
253
|
Capybara::Server.new(MyApplication).boot
|
@@ -519,32 +534,32 @@ rather than driving a web browser.
|
|
519
534
|
|
520
535
|
#### API Driver ####
|
521
536
|
|
522
|
-
The `Kookaburra::APIDriver` is used to interact with an application's
|
523
|
-
web services API. You tell Kookaburra about your API by
|
524
|
-
`Kookaburra::APIDriver` for your application
|
525
|
-
|
526
|
-
|
527
|
-
`Kookaburra::JsonApiDriver`:
|
537
|
+
The `Kookaburra::APIDriver` is used to interact with an application's
|
538
|
+
external web services API. You tell Kookaburra about your API by
|
539
|
+
creating a subclass of `Kookaburra::APIDriver` for your application,
|
540
|
+
specifying how requests should be encoded and decoded, and specifying
|
541
|
+
any headers that should be present on every request.
|
528
542
|
|
529
543
|
# lib/my_app/kookaburra/api_driver.rb
|
530
544
|
|
531
|
-
class MyApp::Kookaburra::APIDriver < Kookaburra::
|
545
|
+
class MyApp::Kookaburra::APIDriver < Kookaburra::APIDriver
|
546
|
+
encode_with { |data| JSON.dump(data) }
|
547
|
+
decode_with { |data| JSON.parse(data) }
|
548
|
+
header 'Content-Type', 'application/json'
|
549
|
+
header 'Accept', 'application/json'
|
550
|
+
|
532
551
|
def create_account(account_data)
|
533
|
-
post '/api/
|
552
|
+
post '/api/accounts', account_data
|
534
553
|
end
|
535
554
|
|
536
555
|
def get_account(id)
|
537
|
-
get '/api/
|
556
|
+
get '/api/accounts/%d' % id
|
538
557
|
end
|
539
558
|
end
|
540
559
|
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
`#delete` in such a way that any Ruby data structure provided as parameters will
|
545
|
-
be appropriately translated to the API's required data format, and any response
|
546
|
-
body from the API request will be translated into a Ruby data structure and
|
547
|
-
returned.
|
560
|
+
The content of your application's APIDriver should consist mainly of
|
561
|
+
mappings between discrete actions and HTTP requests to the specified URL
|
562
|
+
paths.
|
548
563
|
|
549
564
|
#### UI Driver ####
|
550
565
|
|
@@ -623,12 +638,13 @@ You describe the various user interface components by sub-classing
|
|
623
638
|
### The Application Driver Layer ###
|
624
639
|
|
625
640
|
`Kookaburra::APIDriver`, `Kookaburra::UIDriver` and
|
626
|
-
`Kookaburra::UIDriver::UIComponent` rely on the Application Driver layer
|
627
|
-
interact with your application. In the case of the `APIDriver`,
|
628
|
-
the [
|
629
|
-
`UIDriver` and `UIComponent` rely on
|
630
|
-
|
631
|
-
driver for
|
641
|
+
`Kookaburra::UIDriver::UIComponent` rely on the Application Driver layer
|
642
|
+
to interact with your application. In the case of the `APIDriver`,
|
643
|
+
Kookaburra uses the [RestClient] [RestClient] library to send HTTP
|
644
|
+
requests to your application. The `UIDriver` and `UIComponent` rely on
|
645
|
+
whatever is passed to `Kookaburra.new` as the `:browser` option.
|
646
|
+
Presently, we have only used Capybara as the application driver for
|
647
|
+
Kookaburra.
|
632
648
|
|
633
649
|
It's possible that something other than Capybara could be passed in, as long as
|
634
650
|
that something presented the same API. In reality, using something other than
|
@@ -664,4 +680,4 @@ further details.
|
|
664
680
|
[RSpec]: http://rspec.info "RSpec.info: home"
|
665
681
|
[Cucumber]: http://cukes.info/ "Cucumber - Making BDD fun"
|
666
682
|
[Pull Request]: https://github.com/projectdx/kookaburra/pull/new/master "Send a pull request - GitHub"
|
667
|
-
[
|
683
|
+
[RestClient]: https://github.com/archiloque/rest-client "archiloque/rest-client -GitHub"
|
data/Rakefile
CHANGED
@@ -29,8 +29,25 @@ require 'rspec/core/rake_task'
|
|
29
29
|
|
30
30
|
task :default => :spec
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
# TODO: figure out how to get rake task working under JRuby and rbenv
|
33
|
+
if defined?(JRUBY_VERSION) && ENV['RBENV_VERSION']
|
34
|
+
desc 'Run specs'
|
35
|
+
task :spec do
|
36
|
+
msg = <<-EOM
|
37
|
+
********************************************************************************
|
38
|
+
* You appear to be running under JRuby and using rbenv.
|
39
|
+
*
|
40
|
+
* For some reason the `rake spec` command isn't working correctly in
|
41
|
+
* these circumstances. However, you should be able to just run the
|
42
|
+
* `rspec` command directly.
|
43
|
+
********************************************************************************
|
44
|
+
EOM
|
45
|
+
raise msg
|
46
|
+
end
|
47
|
+
else
|
48
|
+
desc 'Run specs'
|
49
|
+
RSpec::Core::RakeTask.new
|
50
|
+
end
|
34
51
|
|
35
52
|
require 'reek/rake/task'
|
36
53
|
Reek::Rake::Task.new do |t|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/kookaburra.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "kookaburra"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["John Wilger", "Sam Livingston-Gray", "Ravi Gadad"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-10-14"
|
13
13
|
s.description = "Cucumber + Capybara = Kookaburra? It made sense at the time."
|
14
14
|
s.email = "johnwilger@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -35,7 +35,6 @@ Gem::Specification.new do |s|
|
|
35
35
|
"lib/kookaburra/dependency_accessor.rb",
|
36
36
|
"lib/kookaburra/exceptions.rb",
|
37
37
|
"lib/kookaburra/given_driver.rb",
|
38
|
-
"lib/kookaburra/json_api_driver.rb",
|
39
38
|
"lib/kookaburra/mental_model.rb",
|
40
39
|
"lib/kookaburra/mental_model_matcher.rb",
|
41
40
|
"lib/kookaburra/test_helpers.rb",
|
@@ -47,7 +46,6 @@ Gem::Specification.new do |s|
|
|
47
46
|
"spec/integration/test_a_rack_application_spec.rb",
|
48
47
|
"spec/kookaburra/api_driver_spec.rb",
|
49
48
|
"spec/kookaburra/configuration_spec.rb",
|
50
|
-
"spec/kookaburra/json_api_driver_spec.rb",
|
51
49
|
"spec/kookaburra/mental_model_matcher_spec.rb",
|
52
50
|
"spec/kookaburra/mental_model_spec.rb",
|
53
51
|
"spec/kookaburra/test_helpers_spec.rb",
|
@@ -70,39 +68,42 @@ Gem::Specification.new do |s|
|
|
70
68
|
s.specification_version = 3
|
71
69
|
|
72
70
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
73
|
-
s.add_runtime_dependency(%q<
|
74
|
-
s.
|
71
|
+
s.add_runtime_dependency(%q<rest-client>, [">= 0"])
|
72
|
+
s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
|
75
73
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
76
74
|
s.add_development_dependency(%q<capybara>, [">= 0"])
|
77
75
|
s.add_development_dependency(%q<yard>, [">= 0"])
|
78
|
-
s.add_development_dependency(%q<
|
76
|
+
s.add_development_dependency(%q<kramdown>, [">= 0"])
|
79
77
|
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
80
78
|
s.add_development_dependency(%q<reek>, [">= 0"])
|
81
79
|
s.add_development_dependency(%q<sinatra>, [">= 0"])
|
82
|
-
s.add_development_dependency(%q<find_a_port>, [">=
|
80
|
+
s.add_development_dependency(%q<find_a_port>, [">= 0"])
|
81
|
+
s.add_development_dependency(%q<json>, [">= 0"])
|
83
82
|
else
|
84
|
-
s.add_dependency(%q<
|
85
|
-
s.add_dependency(%q<
|
83
|
+
s.add_dependency(%q<rest-client>, [">= 0"])
|
84
|
+
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
86
85
|
s.add_dependency(%q<rspec>, [">= 0"])
|
87
86
|
s.add_dependency(%q<capybara>, [">= 0"])
|
88
87
|
s.add_dependency(%q<yard>, [">= 0"])
|
89
|
-
s.add_dependency(%q<
|
88
|
+
s.add_dependency(%q<kramdown>, [">= 0"])
|
90
89
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
91
90
|
s.add_dependency(%q<reek>, [">= 0"])
|
92
91
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
93
|
-
s.add_dependency(%q<find_a_port>, [">=
|
92
|
+
s.add_dependency(%q<find_a_port>, [">= 0"])
|
93
|
+
s.add_dependency(%q<json>, [">= 0"])
|
94
94
|
end
|
95
95
|
else
|
96
|
-
s.add_dependency(%q<
|
97
|
-
s.add_dependency(%q<
|
96
|
+
s.add_dependency(%q<rest-client>, [">= 0"])
|
97
|
+
s.add_dependency(%q<jruby-openssl>, [">= 0"])
|
98
98
|
s.add_dependency(%q<rspec>, [">= 0"])
|
99
99
|
s.add_dependency(%q<capybara>, [">= 0"])
|
100
100
|
s.add_dependency(%q<yard>, [">= 0"])
|
101
|
-
s.add_dependency(%q<
|
101
|
+
s.add_dependency(%q<kramdown>, [">= 0"])
|
102
102
|
s.add_dependency(%q<jeweler>, [">= 0"])
|
103
103
|
s.add_dependency(%q<reek>, [">= 0"])
|
104
104
|
s.add_dependency(%q<sinatra>, [">= 0"])
|
105
|
-
s.add_dependency(%q<find_a_port>, [">=
|
105
|
+
s.add_dependency(%q<find_a_port>, [">= 0"])
|
106
|
+
s.add_dependency(%q<json>, [">= 0"])
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
@@ -1,91 +1,205 @@
|
|
1
|
+
require 'restclient'
|
1
2
|
require 'kookaburra/exceptions'
|
2
|
-
require 'delegate'
|
3
|
-
require 'patron'
|
4
3
|
|
5
4
|
class Kookaburra
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# Communicate with a Web Services API
|
6
|
+
#
|
7
|
+
# You will create a subclass of `APIDriver` in your testing
|
8
|
+
# implementation to be used with you subclass of
|
9
|
+
# `Kookaburra::GivenDriver`. While the `GivenDriver` implements the
|
10
|
+
# "business domain" DSL for setting up your application state, the
|
11
|
+
# `APIDriver` maps discreet operations to your application's web
|
12
|
+
# service API and can (optionally) handle encoding input data and
|
13
|
+
# decoding response bodies to and from your preferred serialization
|
14
|
+
# format.
|
15
|
+
class APIDriver
|
16
|
+
class << self
|
17
|
+
# Serializes input data
|
18
|
+
#
|
19
|
+
# If specified, any input data provided to `APIDriver#post`,
|
20
|
+
# `APIDriver#put` or `APIDriver#request` will be processed through
|
21
|
+
# this function prior to being sent to the HTTP server.
|
22
|
+
#
|
23
|
+
# @yieldparam data [Object] The data parameter that was passed to
|
24
|
+
# the request method
|
25
|
+
# @yieldreturn [String] The text to be used as the request body
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# class MyAPIDriver < Kookaburra::APIDriver
|
29
|
+
# encode_with { |data| JSON.dump(data) }
|
30
|
+
# # ...
|
31
|
+
# end
|
32
|
+
def encode_with(&block)
|
33
|
+
define_method(:encode) do |data|
|
34
|
+
return if data.nil?
|
35
|
+
block.call(data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Deserialize response body
|
40
|
+
#
|
41
|
+
# If specified, the response bodies of all requests made using
|
42
|
+
# this `APIDriver` will be processed through this function prior
|
43
|
+
# to being returned.
|
44
|
+
#
|
45
|
+
# @yieldparam data [String] The response body sent by the HTTP
|
46
|
+
# server
|
47
|
+
#
|
48
|
+
# @yieldreturn [Object] The result of parsing the response body
|
49
|
+
# through this function
|
50
|
+
#
|
51
|
+
# @example
|
52
|
+
# class MyAPIDriver < Kookaburra::APIDriver
|
53
|
+
# decode_with { |data| JSON.parse(data) }
|
54
|
+
# # ...
|
55
|
+
# end
|
56
|
+
def decode_with(&block)
|
57
|
+
define_method(:decode) do |data|
|
58
|
+
block.call(data)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Set custom HTTP headers
|
63
|
+
#
|
64
|
+
# Can be called multiple times to set HTTP headers that will be
|
65
|
+
# provided with every request made by the `APIDriver`.
|
66
|
+
#
|
67
|
+
# @param [String] name The name of the header, e.g. 'Content-Type'
|
68
|
+
# @param [String] value The value to which the header is set
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# class MyAPIDriver < Kookaburra::APIDriver
|
72
|
+
# header 'Content-Type', 'application/json'
|
73
|
+
# header 'Accept', 'application/json'
|
74
|
+
# # ...
|
75
|
+
# end
|
76
|
+
def header(name, value)
|
77
|
+
headers[name] = value
|
78
|
+
end
|
79
|
+
|
80
|
+
# Used to retrieve the list of headers within the instance. Not
|
81
|
+
# intended to be used elsewhere.
|
82
|
+
#
|
83
|
+
# @private
|
84
|
+
def headers
|
85
|
+
@headers ||= {}
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Create a new `APIDriver` instance
|
10
90
|
#
|
11
91
|
# @param [Kookaburra::Configuration] configuration
|
12
|
-
# @param [
|
13
|
-
|
14
|
-
|
15
|
-
|
92
|
+
# @param [RestClient] http_client (optional) Generally only
|
93
|
+
# overriden when testing Kookaburra itself
|
94
|
+
def initialize(configuration, http_client = RestClient)
|
95
|
+
@configuration = configuration
|
96
|
+
@http_client = http_client
|
16
97
|
end
|
17
98
|
|
18
|
-
#
|
99
|
+
# Convenience method to make a POST request
|
19
100
|
#
|
20
|
-
# @
|
21
|
-
|
22
|
-
|
23
|
-
# @option options [Integer] :expected_response_status (201) The HTTP status
|
24
|
-
# code that you expect the server to respond with.
|
25
|
-
# @raise [Kookaburra::UnexpectedResponse] raised if the HTTP status of the
|
26
|
-
# response does not match the `:expected_response_status`
|
27
|
-
def post(path, data, options = {})
|
28
|
-
request(:post, path, options, data)
|
101
|
+
# @see APIDriver#request
|
102
|
+
def post(path, data)
|
103
|
+
request(:post, path, data)
|
29
104
|
end
|
30
105
|
|
31
|
-
#
|
106
|
+
# Convenience method to make a PUT request
|
32
107
|
#
|
33
|
-
# @
|
34
|
-
|
35
|
-
|
36
|
-
# @option options [Integer] :expected_response_status (201) The HTTP status
|
37
|
-
# code that you expect the server to respond with.
|
38
|
-
# @raise [Kookaburra::UnexpectedResponse] raised if the HTTP status of the
|
39
|
-
# response does not match the `:expected_response_status`
|
40
|
-
def put(path, data, options = {})
|
41
|
-
request(:put, path, options, data)
|
108
|
+
# @see APIDriver#request
|
109
|
+
def put(path, data)
|
110
|
+
request(:put, path, data)
|
42
111
|
end
|
43
112
|
|
44
|
-
#
|
113
|
+
# Convenience method to make a GET request
|
45
114
|
#
|
46
|
-
# @
|
47
|
-
|
48
|
-
|
49
|
-
# @raise [Kookaburra::UnexpectedResponse] raised if the HTTP status of the
|
50
|
-
# response does not match the `:expected_response_status`
|
51
|
-
def get(path, options = {})
|
52
|
-
request(:get, path, options)
|
115
|
+
# @see APIDriver#request
|
116
|
+
def get(path)
|
117
|
+
request(:get, path)
|
53
118
|
end
|
54
119
|
|
55
|
-
#
|
120
|
+
# Convenience method to make a DELETE request
|
121
|
+
#
|
122
|
+
# @see APIDriver#request
|
123
|
+
def delete(path)
|
124
|
+
request(:delete, path)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Make an HTTP request
|
128
|
+
#
|
129
|
+
# If you need to make a request other than the typical GET, POST,
|
130
|
+
# PUT and DELETE, you can use this method directly.
|
131
|
+
#
|
132
|
+
# This *will* follow redirects when the server's response code is in
|
133
|
+
# the 3XX range. If the response is a 303, the request will be
|
134
|
+
# transformed into a GET request.
|
56
135
|
#
|
57
|
-
# @
|
58
|
-
# @
|
59
|
-
#
|
60
|
-
# @
|
61
|
-
#
|
62
|
-
|
63
|
-
|
136
|
+
# @see APIDriver.encode_with
|
137
|
+
# @see APIDriver.decode_with
|
138
|
+
# @see APIDriver.header
|
139
|
+
# @see APIDriver#get
|
140
|
+
# @see APIDriver#post
|
141
|
+
# @see APIDriver#put
|
142
|
+
# @see APIDriver#delete
|
143
|
+
#
|
144
|
+
# @param [Symbol] method The HTTP verb to use with the request
|
145
|
+
# @param [String] path The path to request. Will be joined with the
|
146
|
+
# `Kookaburra::Configuration#app_host` setting to build the
|
147
|
+
# URL unless a full URL is specified here.
|
148
|
+
# @param [Object] data The data to be posted in the request body. If
|
149
|
+
# an encoder was specified, this can be any type of object as
|
150
|
+
# long as the encoder can serialize it into a String. If no
|
151
|
+
# encoder was specified, then this can be one of:
|
152
|
+
#
|
153
|
+
# * a String - will be passed as is
|
154
|
+
# * a Hash - will be encoded as normal HTTP form params
|
155
|
+
# * a Hash containing references to one or more Files - will
|
156
|
+
# set the content type to multipart/form-data
|
157
|
+
#
|
158
|
+
# @return [Object] The response body returned by the server. If a
|
159
|
+
# decoder was specified, this will return the result of
|
160
|
+
# parsing the response body through the decoder function.
|
161
|
+
#
|
162
|
+
# @raise [Kookaburra::UnexpectedResponse] Raised if the HTTP
|
163
|
+
# response received is not in the 2XX-3XX range.
|
164
|
+
def request(method, path, data = nil)
|
165
|
+
data = encode(data)
|
166
|
+
response = @http_client.send(method, url_for(path), *[data, headers].compact)
|
167
|
+
decode(response.body)
|
168
|
+
rescue RestClient::Exception => e
|
169
|
+
raise_unexpected_response(e)
|
64
170
|
end
|
65
171
|
|
66
172
|
private
|
67
173
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
if path.nil?
|
72
|
-
raise ArgumentError, "You must specify a request URL, but it was nil."
|
73
|
-
end
|
74
|
-
args = [type, path, data, options].compact
|
75
|
-
response = __getobj__.send(*args)
|
174
|
+
def headers
|
175
|
+
self.class.headers
|
176
|
+
end
|
76
177
|
|
77
|
-
|
78
|
-
|
178
|
+
def url_for(path)
|
179
|
+
URI.join(base_url, path).to_s
|
79
180
|
end
|
80
181
|
|
81
|
-
def
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
182
|
+
def base_url
|
183
|
+
@configuration.app_host
|
184
|
+
end
|
185
|
+
|
186
|
+
def encode(data)
|
187
|
+
data
|
188
|
+
end
|
189
|
+
|
190
|
+
def decode(data)
|
191
|
+
data
|
192
|
+
end
|
193
|
+
|
194
|
+
def raise_unexpected_response(exception)
|
195
|
+
message = <<-END
|
196
|
+
Unexpected response from server: #{exception.message}
|
197
|
+
|
198
|
+
#{exception.http_body}
|
199
|
+
END
|
200
|
+
new_exception = UnexpectedResponse.new(message)
|
201
|
+
new_exception.set_backtrace(exception.backtrace)
|
202
|
+
raise new_exception
|
89
203
|
end
|
90
204
|
end
|
91
205
|
end
|
@@ -5,7 +5,7 @@ class Kookaburra
|
|
5
5
|
# test preconditions. Unlike {Kookaburra::APIDriver}, which is meant to be a
|
6
6
|
# simple mapping to your application's API, a method in the GivenDriver may be
|
7
7
|
# comprised of several distinct API calls as well as access to Kookaburra's
|
8
|
-
# test data store
|
8
|
+
# test data store via `#mental_model`.
|
9
9
|
#
|
10
10
|
# @abstract Subclass and implement your Given DSL.
|
11
11
|
#
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'kookaburra/test_helpers'
|
2
|
-
require 'kookaburra/
|
2
|
+
require 'kookaburra/api_driver'
|
3
3
|
require 'capybara'
|
4
4
|
require 'thwait'
|
5
5
|
require 'find_a_port'
|
@@ -182,7 +182,12 @@ describe "testing a Rack application with Kookaburra" do
|
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
185
|
-
class MyAPIDriver < Kookaburra::
|
185
|
+
class MyAPIDriver < Kookaburra::APIDriver
|
186
|
+
encode_with { |data| JSON.dump(data) }
|
187
|
+
decode_with { |data| JSON.parse(data) }
|
188
|
+
header 'Content-Type', 'application/json'
|
189
|
+
header 'Accept', 'application/json'
|
190
|
+
|
186
191
|
def create_user(user_data)
|
187
192
|
post '/users', user_data
|
188
193
|
end
|
@@ -315,17 +320,29 @@ describe "testing a Rack application with Kookaburra" do
|
|
315
320
|
end
|
316
321
|
|
317
322
|
before(:all) do
|
318
|
-
|
323
|
+
start_server = lambda {
|
319
324
|
Capybara.server_port = APP_PORT
|
320
325
|
Capybara::Server.new(JsonApiApp.new).boot
|
321
326
|
ThreadsWait.all_waits(Thread.list)
|
327
|
+
}
|
328
|
+
if defined?(JRUBY_VERSION)
|
329
|
+
# Can't `fork` in JRuby. This doesn't provide the state
|
330
|
+
# isolation that you get with forking (AFAIK, I'm no JVM
|
331
|
+
# expert, though), but it lets the tests run and pass.
|
332
|
+
Thread.new { start_server.call }
|
333
|
+
else
|
334
|
+
@rack_server_pid = fork do
|
335
|
+
start_server.call
|
336
|
+
end
|
322
337
|
end
|
323
338
|
sleep 1 # Give the server a chance to start up.
|
324
339
|
end
|
325
340
|
|
326
341
|
after(:all) do
|
327
|
-
|
328
|
-
|
342
|
+
unless defined?(JRUBY_VERSION)
|
343
|
+
Process.kill(9, @rack_server_pid)
|
344
|
+
Process.wait
|
345
|
+
end
|
329
346
|
end
|
330
347
|
|
331
348
|
it "runs the tests against the app" do
|
@@ -1,159 +1,102 @@
|
|
1
1
|
require 'kookaburra/api_driver'
|
2
2
|
|
3
3
|
describe Kookaburra::APIDriver do
|
4
|
+
def url_for(uri)
|
5
|
+
URI.join('http://example.com', uri).to_s
|
6
|
+
end
|
7
|
+
|
4
8
|
let(:configuration) { stub('Configuration', :app_host => 'http://example.com') }
|
5
9
|
|
6
10
|
let(:api) { Kookaburra::APIDriver.new(configuration, client) }
|
7
11
|
|
8
|
-
let(:response) {
|
9
|
-
stub('Patron::Response', :body => 'foo', :status => 200, :url => '/foo')
|
10
|
-
}
|
12
|
+
let(:response) { stub('RestClient::Response', body: 'foo', code: 200) }
|
11
13
|
|
12
|
-
let(:client) {
|
13
|
-
mock('Patron::Session', :post => response, :get => response,
|
14
|
-
:put => response, :delete => response, :base_url= => nil)
|
15
|
-
}
|
16
|
-
|
17
|
-
describe '#initialize' do
|
18
|
-
it 'instantiates a new http client if no :http_client option is passed' do
|
19
|
-
Patron::Session.should_receive(:new).and_return(stub.as_null_object)
|
20
|
-
Kookaburra::APIDriver.new(configuration)
|
21
|
-
end
|
14
|
+
let(:client) { stub('RestClient') }
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
16
|
+
it 'sends POST requests to the server and returns the response body' do
|
17
|
+
client.should_receive(:post).with(url_for('/foo'), 'bar', {}) \
|
18
|
+
.and_return(response)
|
19
|
+
api.post('/foo', 'bar').should == 'foo'
|
27
20
|
end
|
28
21
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
it 'delegates to the http client' do
|
35
|
-
client.should_receive(:post).with('/foo', 'bar', {}) \
|
36
|
-
.and_return(response)
|
37
|
-
api.post('/foo', 'bar')
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'returns the response body' do
|
41
|
-
api.post('/foo', 'bar').should == 'foo'
|
42
|
-
end
|
43
|
-
|
44
|
-
it 'does not raise an UnexpectedResponse if the response status matches the specified expectation' do
|
45
|
-
response.stub!(:status => 666)
|
46
|
-
lambda { api.post('/foo', 'bar', :expected_response_status => 666) } \
|
47
|
-
.should_not raise_error
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'raises an UnexpectedResponse if the response status is not the specified status' do
|
51
|
-
lambda { api.post('/foo', 'bar', :expected_response_status => 666) } \
|
52
|
-
.should raise_error(Kookaburra::UnexpectedResponse,
|
53
|
-
"POST to /foo responded with 201 status, not 666 as expected\n\nfoo")
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'is OK by default with a response status of 201' do
|
57
|
-
lambda { api.post('/foo', 'bar') } \
|
58
|
-
.should_not raise_error(Kookaburra::UnexpectedResponse)
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'is OK by default with a response status of 200' do
|
62
|
-
response.stub!(:status => 200)
|
63
|
-
lambda { api.post('/foo', 'bar') } \
|
64
|
-
.should_not raise_error(Kookaburra::UnexpectedResponse)
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'raises an ArgumentError with a useful message if no request path is specified' do
|
68
|
-
lambda { api.post(nil, 'bar') } \
|
69
|
-
.should raise_error(ArgumentError, "You must specify a request URL, but it was nil.")
|
70
|
-
end
|
22
|
+
it 'sends PUT requests to the server and returns the response body' do
|
23
|
+
client.should_receive(:put).with(url_for('/foo'), 'bar', {}) \
|
24
|
+
.and_return(response)
|
25
|
+
api.put('/foo', 'bar').should == 'foo'
|
71
26
|
end
|
72
27
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
api.put('/foo', 'bar')
|
78
|
-
end
|
79
|
-
|
80
|
-
it 'returns the response body' do
|
81
|
-
api.put('/foo', 'bar').should == 'foo'
|
82
|
-
end
|
83
|
-
|
84
|
-
it 'does not raise an UnexpectedResponse if the response status matches the specified expectation' do
|
85
|
-
response.stub!(:status => 666)
|
86
|
-
lambda { api.put('/foo', 'bar', :expected_response_status => 666) } \
|
87
|
-
.should_not raise_error
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'raises an UnexpectedResponse if the response status is not the specified status' do
|
91
|
-
lambda { api.put('/foo', 'bar', :expected_response_status => 666) } \
|
92
|
-
.should raise_error(Kookaburra::UnexpectedResponse,
|
93
|
-
"PUT to /foo responded with 200 status, not 666 as expected\n\nfoo")
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'raises an ArgumentError with a useful message if no request path is specified' do
|
97
|
-
lambda { api.put(nil, 'bar') } \
|
98
|
-
.should raise_error(ArgumentError, "You must specify a request URL, but it was nil.")
|
99
|
-
end
|
28
|
+
it 'sends GET requests to the server and returns the response body' do
|
29
|
+
client.should_receive(:get).with(url_for('/foo'), {}) \
|
30
|
+
.and_return(response)
|
31
|
+
api.get('/foo').should == 'foo'
|
100
32
|
end
|
101
33
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
api.get('/foo')
|
107
|
-
end
|
108
|
-
|
109
|
-
it 'returns the response body' do
|
110
|
-
api.get('/foo').should == 'foo'
|
111
|
-
end
|
112
|
-
|
113
|
-
it 'does not raise an UnexpectedResponse if the response status matches the specified expectation' do
|
114
|
-
response.stub!(:status => 666)
|
115
|
-
lambda { api.get('/foo', :expected_response_status => 666) } \
|
116
|
-
.should_not raise_error
|
117
|
-
end
|
118
|
-
|
119
|
-
it 'raises an UnexpectedResponse if the response status is not the specified status' do
|
120
|
-
lambda { api.get('/foo', :expected_response_status => 666) } \
|
121
|
-
.should raise_error(Kookaburra::UnexpectedResponse,
|
122
|
-
"GET to /foo responded with 200 status, not 666 as expected\n\nfoo")
|
123
|
-
end
|
124
|
-
|
125
|
-
it 'raises an ArgumentError with a useful message if no request path is specified' do
|
126
|
-
lambda { api.get(nil) } \
|
127
|
-
.should raise_error(ArgumentError, "You must specify a request URL, but it was nil.")
|
128
|
-
end
|
34
|
+
it 'sends DELETE requests to the server and returns the response body' do
|
35
|
+
client.should_receive(:delete).with(url_for('/foo'), {}) \
|
36
|
+
.and_return(response)
|
37
|
+
api.delete('/foo').should == 'foo'
|
129
38
|
end
|
130
39
|
|
131
|
-
describe '
|
132
|
-
|
133
|
-
client.
|
134
|
-
.and_return(response)
|
135
|
-
api.delete('/foo')
|
40
|
+
describe 'any type of HTTP request' do
|
41
|
+
before(:each) do
|
42
|
+
client.stub!(:http_verb => response)
|
136
43
|
end
|
137
44
|
|
138
45
|
it 'returns the response body' do
|
139
|
-
api.
|
140
|
-
end
|
141
|
-
|
142
|
-
it '
|
143
|
-
response.stub!(:
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
46
|
+
api.request(:http_verb, '/foo', 'bar').should == 'foo'
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises an UnexpectedResponse if the request is not successful' do
|
50
|
+
response.stub!(code: 500)
|
51
|
+
client.stub!(:http_verb).and_raise(RestClient::Exception.new(response))
|
52
|
+
lambda { api.request(:http_verb, '/foo') } \
|
53
|
+
.should raise_error(Kookaburra::UnexpectedResponse)
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when custom headers are specified' do
|
57
|
+
let(:api) {
|
58
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
59
|
+
header 'Header-Foo', 'Baz'
|
60
|
+
header 'Header-Bar', 'Bam'
|
61
|
+
end
|
62
|
+
klass.new(configuration, client)
|
63
|
+
}
|
64
|
+
|
65
|
+
it "sets headers on requests" do
|
66
|
+
client.should_receive(:http_verb).with(url_for('/foo'), {}, 'Header-Foo' => 'Baz', 'Header-Bar' => 'Bam')
|
67
|
+
api.request(:http_verb, '/foo', {})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when a custom encoder is specified' do
|
72
|
+
let(:api) {
|
73
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
74
|
+
encode_with { |data| :some_encoded_data }
|
75
|
+
end
|
76
|
+
klass.new(configuration, client)
|
77
|
+
}
|
78
|
+
|
79
|
+
it "encodes input to requests" do
|
80
|
+
client.should_receive(:http_verb) do |_, data, _|
|
81
|
+
data.should == :some_encoded_data
|
82
|
+
response
|
83
|
+
end
|
84
|
+
|
85
|
+
api.request(:http_verb, '/foo', :ruby_data)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'when a custom decoder is specified' do
|
90
|
+
let(:api) {
|
91
|
+
klass = Class.new(Kookaburra::APIDriver) do
|
92
|
+
decode_with { |data| :some_decoded_data }
|
93
|
+
end
|
94
|
+
klass.new(configuration, client)
|
95
|
+
}
|
96
|
+
|
97
|
+
it "decodes response bodies from requests" do
|
98
|
+
api.request(:http_verb, '/foo').should == :some_decoded_data
|
99
|
+
end
|
157
100
|
end
|
158
101
|
end
|
159
102
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kookaburra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,10 +11,10 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-10-14 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
17
|
+
name: rest-client
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
- !ruby/object:Gem::Version
|
31
31
|
version: '0'
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
|
-
name:
|
33
|
+
name: jruby-openssl
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
35
35
|
none: false
|
36
36
|
requirements:
|
37
37
|
- - ! '>='
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '0'
|
40
|
-
type: :
|
40
|
+
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
none: false
|
@@ -94,21 +94,21 @@ dependencies:
|
|
94
94
|
- !ruby/object:Gem::Version
|
95
95
|
version: '0'
|
96
96
|
- !ruby/object:Gem::Dependency
|
97
|
-
name:
|
97
|
+
name: kramdown
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
99
99
|
none: false
|
100
100
|
requirements:
|
101
|
-
- -
|
101
|
+
- - ! '>='
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
none: false
|
108
108
|
requirements:
|
109
|
-
- -
|
109
|
+
- - ! '>='
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
111
|
+
version: '0'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
113
|
name: jeweler
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,7 +164,7 @@ dependencies:
|
|
164
164
|
requirements:
|
165
165
|
- - ! '>='
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
167
|
+
version: '0'
|
168
168
|
type: :development
|
169
169
|
prerelease: false
|
170
170
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -172,7 +172,23 @@ dependencies:
|
|
172
172
|
requirements:
|
173
173
|
- - ! '>='
|
174
174
|
- !ruby/object:Gem::Version
|
175
|
-
version:
|
175
|
+
version: '0'
|
176
|
+
- !ruby/object:Gem::Dependency
|
177
|
+
name: json
|
178
|
+
requirement: !ruby/object:Gem::Requirement
|
179
|
+
none: false
|
180
|
+
requirements:
|
181
|
+
- - ! '>='
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
type: :development
|
185
|
+
prerelease: false
|
186
|
+
version_requirements: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ! '>='
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
176
192
|
description: Cucumber + Capybara = Kookaburra? It made sense at the time.
|
177
193
|
email: johnwilger@gmail.com
|
178
194
|
executables: []
|
@@ -199,7 +215,6 @@ files:
|
|
199
215
|
- lib/kookaburra/dependency_accessor.rb
|
200
216
|
- lib/kookaburra/exceptions.rb
|
201
217
|
- lib/kookaburra/given_driver.rb
|
202
|
-
- lib/kookaburra/json_api_driver.rb
|
203
218
|
- lib/kookaburra/mental_model.rb
|
204
219
|
- lib/kookaburra/mental_model_matcher.rb
|
205
220
|
- lib/kookaburra/test_helpers.rb
|
@@ -211,7 +226,6 @@ files:
|
|
211
226
|
- spec/integration/test_a_rack_application_spec.rb
|
212
227
|
- spec/kookaburra/api_driver_spec.rb
|
213
228
|
- spec/kookaburra/configuration_spec.rb
|
214
|
-
- spec/kookaburra/json_api_driver_spec.rb
|
215
229
|
- spec/kookaburra/mental_model_matcher_spec.rb
|
216
230
|
- spec/kookaburra/mental_model_spec.rb
|
217
231
|
- spec/kookaburra/test_helpers_spec.rb
|
@@ -238,7 +252,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
238
252
|
version: '0'
|
239
253
|
segments:
|
240
254
|
- 0
|
241
|
-
hash: -
|
255
|
+
hash: -1361909781639378810
|
242
256
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
243
257
|
none: false
|
244
258
|
requirements:
|
@@ -1,65 +0,0 @@
|
|
1
|
-
require 'delegate'
|
2
|
-
require 'json'
|
3
|
-
require 'kookaburra/api_driver'
|
4
|
-
|
5
|
-
class Kookaburra
|
6
|
-
# Delegates all methods (by default) to and instance of
|
7
|
-
# {Kookaburra::APIDriver}.
|
8
|
-
#
|
9
|
-
# Expects the application's API to accept and respond with JSON formatted
|
10
|
-
# data. All methods will decode the response body using
|
11
|
-
# `ActiveSupport::JSON.decode`. Methods that take input data ({#post} and
|
12
|
-
# {#put}) will encode the post data using `ActiveSupport::JSON.encode`.
|
13
|
-
class JsonApiDriver < SimpleDelegator
|
14
|
-
#
|
15
|
-
# Sets both the "Content-Type" and "Accept" headers to "application/json".
|
16
|
-
#
|
17
|
-
# @param [Kookaburra::Configuration] configuration
|
18
|
-
# @param [Kookaburra::APIDriver] api_driver (Kookaburra::APIDriver.new)
|
19
|
-
# The APIDriver instance to be delegated to. Changing this is probably
|
20
|
-
# only useful for testing Kookaburra itself.
|
21
|
-
def initialize(configuration, api_driver = nil)
|
22
|
-
api_driver = api_driver || APIDriver.new(configuration)
|
23
|
-
api_driver.headers.merge!(
|
24
|
-
'Content-Type' => 'application/json',
|
25
|
-
'Accept' => 'application/json'
|
26
|
-
)
|
27
|
-
super(api_driver)
|
28
|
-
end
|
29
|
-
|
30
|
-
def post(path, data, *args)
|
31
|
-
request(:post, path, data, *args)
|
32
|
-
end
|
33
|
-
|
34
|
-
def put(path, data, *args)
|
35
|
-
request(:put, path, data, *args)
|
36
|
-
end
|
37
|
-
|
38
|
-
def get(path, *args)
|
39
|
-
request(:get, path, nil, *args)
|
40
|
-
end
|
41
|
-
|
42
|
-
def delete(path, *args)
|
43
|
-
request(:delete, path, nil, *args)
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def request(type, path, data = nil, *args)
|
49
|
-
# don't want to send data to methods that don't accept it
|
50
|
-
args = [path, *([encode(data), args].reject(&:nil?))].flatten
|
51
|
-
|
52
|
-
output = __getobj__.send(type, *args)
|
53
|
-
|
54
|
-
decode(output)
|
55
|
-
end
|
56
|
-
|
57
|
-
def encode(data)
|
58
|
-
JSON.dump(data) unless data.nil?
|
59
|
-
end
|
60
|
-
|
61
|
-
def decode(data)
|
62
|
-
JSON.parse(data)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
@@ -1,95 +0,0 @@
|
|
1
|
-
require 'kookaburra/json_api_driver'
|
2
|
-
|
3
|
-
describe Kookaburra::JsonApiDriver do
|
4
|
-
let(:response) { '{"foo":"bar"}' }
|
5
|
-
|
6
|
-
let(:api) {
|
7
|
-
stub('APIDriver', :get => response, :post => response, :put => response,
|
8
|
-
:delete => response, :headers => {})
|
9
|
-
}
|
10
|
-
|
11
|
-
let(:configuration) {
|
12
|
-
stub('Configuration')
|
13
|
-
}
|
14
|
-
|
15
|
-
let(:json) { Kookaburra::JsonApiDriver.new(stub('Configuration'), api) }
|
16
|
-
|
17
|
-
describe '#initialize' do
|
18
|
-
it 'instantiates a new APIDriver if no :api_driver option is passed' do
|
19
|
-
Kookaburra::APIDriver.should_receive(:new) \
|
20
|
-
.with(configuration) \
|
21
|
-
.and_return(api)
|
22
|
-
Kookaburra::JsonApiDriver.new(configuration)
|
23
|
-
end
|
24
|
-
|
25
|
-
it 'does not instantiate a new APIDriver if an :api_driver option is passed' do
|
26
|
-
Kookaburra::APIDriver.should_receive(:new).never
|
27
|
-
Kookaburra::JsonApiDriver.new(configuration, api)
|
28
|
-
end
|
29
|
-
|
30
|
-
it 'sets appropriate headers for a JSON API request' do
|
31
|
-
Kookaburra::JsonApiDriver.new(configuration, api)
|
32
|
-
api.headers.should == {
|
33
|
-
'Content-Type' => 'application/json',
|
34
|
-
'Accept' => 'application/json'
|
35
|
-
}
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'delegates to a Kookaburra::APIDriver by default' do
|
40
|
-
api.stub!(:foo => :bar)
|
41
|
-
json.foo.should == :bar
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '#post' do
|
45
|
-
it 'delegates to the api driver as a JSON request' do
|
46
|
-
api.should_receive(:post) \
|
47
|
-
.with('/foo', '{"foo":"bar"}') \
|
48
|
-
.and_return('{"baz":"bam"}')
|
49
|
-
json.post('/foo', 'foo' => 'bar')
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'returns the JSON-decoded response body' do
|
53
|
-
json.post('/foo', :bar => :baz).should == {'foo' => 'bar'}
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
describe '#put' do
|
58
|
-
it 'delegates to the api driver as a JSON request' do
|
59
|
-
api.should_receive(:put) \
|
60
|
-
.with('/foo', '{"foo":"bar"}') \
|
61
|
-
.and_return('{"baz":"bam"}')
|
62
|
-
json.put('/foo', 'foo' => 'bar')
|
63
|
-
end
|
64
|
-
|
65
|
-
it 'returns the JSON-decoded response body' do
|
66
|
-
json.put('/foo', :bar => :baz).should == {'foo' => 'bar'}
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe '#get' do
|
71
|
-
it 'delegates to the api driver as a JSON request' do
|
72
|
-
api.should_receive(:get) \
|
73
|
-
.with('/foo') \
|
74
|
-
.and_return('{"baz":"bam"}')
|
75
|
-
json.get('/foo')
|
76
|
-
end
|
77
|
-
|
78
|
-
it 'returns the JSON-decoded response body' do
|
79
|
-
json.get('/foo').should == {'foo' => 'bar'}
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
describe '#delete' do
|
84
|
-
it 'delegates to the api driver as a JSON request' do
|
85
|
-
api.should_receive(:delete) \
|
86
|
-
.with('/foo') \
|
87
|
-
.and_return('{"baz":"bam"}')
|
88
|
-
json.delete('/foo')
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'returns the JSON-decoded response body' do
|
92
|
-
json.delete('/foo').should == {'foo' => 'bar'}
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|