kookaburra 0.18.3 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,7 +5,7 @@ gem 'basic_object' unless defined?(BasicObject)
5
5
 
6
6
  gem 'i18n'
7
7
  gem 'activesupport', '>= 3.0'
8
- gem 'rack-test'
8
+ gem 'patron'
9
9
 
10
10
  # Add dependencies to develop your gem here.
11
11
  # Include everything needed to run rake, tests, features, etc.
data/Gemfile.lock CHANGED
@@ -27,6 +27,7 @@ GEM
27
27
  mime-types (1.17.2)
28
28
  multi_json (1.1.0)
29
29
  nokogiri (1.5.0)
30
+ patron (0.4.18)
30
31
  rack (1.4.1)
31
32
  rack-protection (1.2.0)
32
33
  rack
@@ -79,7 +80,7 @@ DEPENDENCIES
79
80
  capybara
80
81
  i18n
81
82
  jeweler
82
- rack-test
83
+ patron
83
84
  rcov
84
85
  redcarpet (~> 1.0)
85
86
  reek
data/README.markdown CHANGED
@@ -3,20 +3,6 @@
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
- ## WARNING: Significant Changes since 0.14.x ##
7
-
8
- As of 0.15.0, Kookaburra has been rewritten from the ground up. The original
9
- (up through 0.14.x) version was extracted from another project in which the
10
- testing library was being used. Unfortunately, this meant that the code in
11
- Kookaburra itself did not have very good test coverage, because it was being
12
- tested indirectly by the fact of its usage in the other project. What we've
13
- found is that a *lot* of complexity was sneaking into Kookaburra due to its
14
- having been developed without much focused TDD.
15
-
16
- Starting with 0.15.0, we are treating the previous versions as a spike. They
17
- were really useful for learning about the approach, but the code has enough
18
- design flaws that its best just to toss it.
19
-
20
6
  ## Installation ##
21
7
 
22
8
  Kookaburra is available as a Rubygem and [published on Rubygems.org] [Kookaburra Gem],
@@ -34,51 +20,45 @@ following:
34
20
  ## Setup ##
35
21
 
36
22
  Kookaburra abstracts some common patterns for implementing the Window Driver
37
- pattern for tests of Ruby web applications built on [Rack] [Rack]. You will need
38
- to tell Kookaburra which classes contain the specific Domain Driver
39
- implementations for your application as well as which driver to use for running
40
- the tests (currently only tested with [Capybara] [Capybara]).
41
-
42
- ### ActiveRecord and Database Transactions ###
43
-
44
- Kookaburra currently uses Rack::Test as the underlying implementation for its
45
- APIDriver classes. This poses a problem when you want to run your UI tests via a
46
- driver other than Rack::Test such as Selenium, because the APIDriver sets up
47
- your test data using a different database connection than the process that runs
48
- the server against which Selenium executes, and they do not have access to the
49
- same transaction.
50
-
51
- One way to handle this problem is to force ActiveRecord to use the same
52
- connection in both the main thread and the thread that is spun up by Capybara to
53
- run the application for Selenium testing. You can do so by requiring
54
- `kookaburra/utils/active_record_shared_connection` within your Kookaburra setup.
55
-
56
- In the near future, we plan to change Kookaburra to execute *both* its APIDriver
57
- and UIDriver against an actual server and ditch Rack::Test. Not only will this
58
- help avoid this specific problem, but it will move towards the goal of being
59
- able to (optionally) run these tests on a completely different machine than the
60
- running application.
23
+ testing pattern for web applications. You will need to tell Kookaburra which
24
+ classes contain the specific Domain Driver implementations for your application
25
+ as well as which driver to use for running the tests (currently only tested with
26
+ [Capybara] [Capybara]).
27
+
28
+ Kookaburra is designed to run tests agains a remote web server (although that
29
+ server could be running on the same machine, it doesn't need to be), and it is
30
+ the responsibility of the test implementation to ensure that the server is
31
+ running. Take a look at Kookaburra's own integration specs for one example of
32
+ how to achieve this for a [Rack-based] [Rack] application. (Note that you cannot
33
+ easily start the application server in a seperate thread. Because Ruby uses
34
+ green threads, the HTTP library used in the APIDriver will block while making
35
+ its requests and prevent the application server thread from responding.)
36
+
37
+ The fact that Kookaburra runs against a remote server means that *it is not
38
+ limited to testing only Ruby web applications*. As long as your application
39
+ exposes a web-service API for use by the GivenDriver and an HTML user interface
40
+ for use by the UIDriver, you can use Kookaburra to test it. Also, as long as
41
+ you're careful with both your application and test designs, you're not limited
42
+ to running your tests only in an isolated testing environment; you could run the
43
+ same test suite you use for development against your production systems and even
44
+ repurpose your Kookaburra-based tests for load-testing and similar applications.
61
45
 
62
46
  ### RSpec ###
63
47
 
64
48
  For [RSpec] [RSpec] integration tests, just add the following to
65
49
  `spec/support/kookaburra_setup.rb`:
66
50
 
67
- # only if using ActiveRecord and a browser driver other than Rack::Test for
68
- # UI testing
69
- require 'kookaburra/utils/active_record_shared_connection'
70
-
71
51
  require 'kookaburra/test_helpers'
72
- require 'my_app/kookaburra/api_driver'
73
52
  require 'my_app/kookaburra/given_driver'
74
53
  require 'my_app/kookaburra/ui_driver'
75
54
 
55
+ # :app_host below should be set to whatever the root URL of your running
56
+ # application is.
76
57
  Kookaburra.configuration = {
77
- :api_driver_class => MyApp::Kookaburra::APIDriver,
78
58
  :given_driver_class => MyApp::Kookaburra::GivenDriver,
79
59
  :ui_driver_class => MyApp::Kookaburra::UIDriver,
60
+ :app_host => 'http://my_app.example.com:1234',
80
61
  :browser => Capybara,
81
- :rack_app => Capybara.app,
82
62
  :server_error_detection => { |browser|
83
63
  browser.has_css?('head title', :text => 'Internal Server Error')
84
64
  }
@@ -92,21 +72,17 @@ For [RSpec] [RSpec] integration tests, just add the following to
92
72
 
93
73
  For [Cucumber] [Cucumber], add the following to `features/support/kookaburra_setup.rb`:
94
74
 
95
- # only if using ActiveRecord and a browser driver other than Rack::Test for
96
- # UI testing
97
- require 'kookaburra/utils/active_record_shared_connection'
98
-
99
75
  require 'kookaburra/test_helpers'
100
- require 'my_app/kookaburra/api_driver'
101
76
  require 'my_app/kookaburra/given_driver'
102
77
  require 'my_app/kookaburra/ui_driver'
103
78
 
79
+ # :app_host below should be set to whatever the root URL of your running
80
+ # application is.
104
81
  Kookaburra.configuration = {
105
- :api_driver_class => MyApp::Kookaburra::APIDriver,
106
82
  :given_driver_class => MyApp::Kookaburra::GivenDriver,
107
83
  :ui_driver_class => MyApp::Kookaburra::UIDriver,
84
+ :app_host => 'http://my_app.example.com:1234',
108
85
  :browser => Capybara,
109
- :rack_app => Capybara.app,
110
86
  :server_error_detection => { |browser|
111
87
  browser.has_css?('head title', :text => 'Internal Server Error')
112
88
  }
@@ -119,10 +95,10 @@ Cucumber step definitions.
119
95
 
120
96
  ## Defining Your Testing DSL ##
121
97
 
122
- Kookaburra attempts to extract some common patterns that make it easier to use
123
- the Window Driver pattern along with various Ruby testing frameworks, but you
124
- still need to define your own testing DSL. An acceptance testing stack using
125
- Kookaburra has the following layers:
98
+ Kookaburra extracts some common patterns that make it easier to use the Window
99
+ Driver pattern along with various Ruby testing frameworks, but you still need to
100
+ define your own testing DSL. An acceptance testing stack using Kookaburra has
101
+ the following layers:
126
102
 
127
103
  1. The **Business Specification Language** (Cucumber scenarios or other
128
104
  spcification documents)
@@ -130,7 +106,7 @@ Kookaburra has the following layers:
130
106
  etc.)
131
107
  3. The **Domain Driver** (Kookaburra::GivenDriver and Kookaburra::UIDriver)
132
108
  4. The **Window Driver** (Kookaburra::UIDriver::UIComponent)
133
- 5. The **Application Driver** (Capybara and Kookaburra::RackDriver)
109
+ 5. The **Application Driver** (Capybara and Kookaburra::APIDriver)
134
110
 
135
111
  ### The Business Specification Language ###
136
112
 
@@ -163,7 +139,9 @@ Note that the scenario is focused on business concepts versus interface details,
163
139
  i.e. you "choose to check out" rather than "click on the checkout button". If
164
140
  for some reason your e-commerce system was going to be a terminal application
165
141
  rather than a web application, you would not need to change this scenario at
166
- all, because the actual business concepts described would not change.
142
+ all, because the actual business concepts described would not change (and
143
+ although Kookaburra's focus is on testing web applications, it could likely be
144
+ adapted to other environments.)
167
145
 
168
146
  ### The Test Implementation ###
169
147
 
@@ -173,19 +151,19 @@ definitions, RSpec example blocks, Test::Unit tests, etc. At this layer, your
173
151
  code orchestrates calls into the Domain Driver to mimic user interactions under
174
152
  various conditions and make assertions about the results.
175
153
 
176
- **Test assertions always belong within the test implementation layer.** Some testing
177
- frameworks such as RSpec add methods like `#should` to `Object`, which has the
178
- effect of poisoning the entire Ruby namespace with these methods---if you are
179
- using RSpec, you can call `#should` anywhere in your code and it will work when
180
- RSpec is loaded. Do not be tempted to call a testing library's Object decorators
181
- anywhere outside of your test implementation (such as within `UIDriver` or
182
- `UIComponent` subclasses.) Doing so will tightly couple your Domain Driver
183
- and/or Window Driver implementation to a specific testing library.
154
+ **Test assertions always belong within the test implementation layer.** Some
155
+ testing frameworks such as RSpec add methods like `#should` to `Object`, which
156
+ has the effect of poisoning the entire Ruby namespace with these methods---if
157
+ you are using RSpec, you can call `#should` anywhere in your code and it will
158
+ work when RSpec is loaded. Do not be tempted to call a testing library's Object
159
+ decorators anywhere outside of your test implementation (such as within
160
+ `UIDriver` or `UIComponent` subclasses.) Doing so will tightly couple your
161
+ Domain Driver and/or Window Driver implementation to a specific testing library.
184
162
 
185
- `Kookaburra::UIDriver::UIComponent` does provide an `#assert` method for use
186
- inside your own UIComponents. This method exists to verify preconditions and
187
- provide more informative error messages; it is not intended to be used to make
188
- test verifications.
163
+ `Kookaburra::UIDriver::UIComponent` provides an `#assert` method for use inside
164
+ your own UIComponents. This method exists to verify preconditions and provide
165
+ more informative error messages; it is not intended to be used for test
166
+ verifications.
189
167
 
190
168
  Given the Cucumber scenario above, here is how the test implementation layer
191
169
  might look:
@@ -287,60 +265,36 @@ top-level drivers: the `GivenDriver` (available via `#given`) used to set up
287
265
  state for your tests and the UIDriver (available via `#ui`) for describing the
288
266
  tasks that a user can accomplish with the application.
289
267
 
290
- #### Test Data ####
268
+ #### Mental Model ####
291
269
 
292
- `Kookaburra::TestData` is the component via which the `GivenDriver` and the
293
- `UIDriver` share information. For instance, if you create a user account via the
294
- `GivenDriver`, you would store the login credentials for that account in the
295
- `TestData` instance, so the `UIDriver` knows what to use when you tell it to
296
- `#sign_in`. This is what allows the Cucumber step definitions to remain free
297
- from explicitly shared state.
270
+ `Kookaburra::MentalModel` is the component via which the `GivenDriver` and the
271
+ `UIDriver` share information, and it is intended to represent your application
272
+ user's mental picture of the data they are working with. For instance, if you
273
+ create a user account via the `GivenDriver`, you would store the login
274
+ credentials for that account in the `MentalModel` instance, so the `UIDriver`
275
+ knows what to use when you tell it to `#sign_in`. This is what allows the
276
+ Cucumber step definitions to remain free from explicitly shared state.
298
277
 
299
- Kookaburra automatically configures your `GivenDriver` and your `UIDriver` to share
300
- a `TestData` instance, which is available to both of them via their `#test_data`
301
- method.
278
+ Kookaburra automatically configures your `GivenDriver` and your `UIDriver` to
279
+ share a `MentalModel` instance, which is available to both of them via their
280
+ `#mental_model` method.
302
281
 
303
- The `TestData` instance will return a `TestData::Collection` for any method
304
- called on the object. The `TestData::Collection` object behaves like a `Hash`
282
+ The `MentalModel` instance will return a `MentalModel::Collection` for any method
283
+ called on the object. The `MentalModel::Collection` object behaves like a `Hash`
305
284
  for the most part, however it will raise a `Kookaburra::UnknownKeyError` if you
306
285
  try to access a key that has not yet been assigned a value.
307
286
 
308
- Here's a quick example of TestData behavor:
287
+ Here's a quick example of MentalModel behavor:
309
288
 
310
- test_data = TestData.new
289
+ mental_model = MentalModel.new
311
290
 
312
- test_data.widgets[:widget_a] = {'name' => 'Widget A'}
291
+ mental_model.widgets[:widget_a] = {'name' => 'Widget A'}
313
292
 
314
- test_data.widgets[:widget_a]
293
+ mental_model.widgets[:widget_a]
315
294
  #=> {'name' => 'Widget A'}
316
295
 
317
296
  # this will raise a Kookaburra::UnknownKeyError
318
- test_data.widgets[:widget_b]
319
-
320
- #### API Driver ####
321
-
322
- The `Kookaburra::APIDriver` is used to interact with an application's external
323
- web services API. You tell Kookaburra about your API by creating a subclass of
324
- `Kookaburra::APIDriver` for your application. Because different applications may
325
- implement different types of APIs, Kookaburra will provide more than one base
326
- APIDriver class. At the moment, only a JSON API is supported via
327
- `Kookaburra::JsonApiDriver`:
328
-
329
- # lib/my_app/kookaburra/api_driver.rb
330
-
331
- class MyApp::Kookaburra::APIDriver < Kookaburra::JsonApiDriver
332
- def create_account(account_data)
333
- post '/api/v1/accounts', account_data
334
- end
335
- end
336
-
337
- Regardless of the type of APIDriver subclass, the contents of your application's
338
- APIDriver should consist mainly of mappings between discrete actions and HTTP
339
- requests to the specified URL paths. Each driver will implement `#post`, `#get`,
340
- `#put`, `#head`, and `#delete` in such a way that any Ruby data structure
341
- provided as parameters will be appropriately translated to the API's required
342
- data format, and any response body from the API request will be translated into
343
- a Ruby data structure and returned.
297
+ mental_model.widgets[:widget_b]
344
298
 
345
299
  #### Given Driver ####
346
300
 
@@ -353,6 +307,11 @@ for your application:
353
307
  # lib/my_app/kookaburra/given_driver.rb
354
308
 
355
309
  class MyApp::Kookaburra::GivenDriver < Kookaburra::GivenDriver
310
+ # Specify the APIDriver to use
311
+ def api
312
+ @api ||= MyApp::Kookaburra::APIDriver.new(:app_host => initialization_options[:app_host])
313
+ end
314
+
356
315
  def existing_account(nickname)
357
316
  account_data = {'display_name' => 'John Doe', 'password' => 'a password'}
358
317
  account_data['username'] = "test-user-#{`uuidgen`.strip}"
@@ -361,34 +320,52 @@ for your application:
361
320
  result = api.create_account(account_data)
362
321
 
363
322
  # merge in the password, since API (hopefully!) doesn't return it, and
364
- # store details in the TestData instance
323
+ # store details in the MentalModel instance
365
324
  result.merge!('password' => account_data['password'])
366
- test_data.accounts[nickname] = account_details
325
+ mental_model.accounts[nickname] = account_details
326
+ end
327
+ end
328
+
329
+ Although there is nothing that actually *prevents* you from interacting with the
330
+ UI in the `GivenDriver`, you should avoid doing so. The `GivenDriver`'s purpose
331
+ is to describe state that exists *before* the user interaction that is being
332
+ tested. Although this state may be the result of a previous user interaction,
333
+ your tests will be much, much faster if you create this state via API calls
334
+ rather than driving a web browser.
335
+
336
+ #### API Driver ####
337
+
338
+ The `Kookaburra::APIDriver` is used to interact with an application's external
339
+ web services API. You tell Kookaburra about your API by creating a subclass of
340
+ `Kookaburra::APIDriver` for your application. Because different applications may
341
+ implement different types of APIs, Kookaburra will provide more than one base
342
+ APIDriver class. At the moment, only a JSON API is supported via
343
+ `Kookaburra::JsonApiDriver`:
344
+
345
+ # lib/my_app/kookaburra/api_driver.rb
346
+
347
+ class MyApp::Kookaburra::APIDriver < Kookaburra::JsonApiDriver
348
+ def create_account(account_data)
349
+ post '/api/v1/accounts', account_data
350
+ end
351
+
352
+ def get_account(id)
353
+ get '/api/v1/accounts/%d' % id
367
354
  end
368
355
  end
369
356
 
370
- Although there is nothing that actually *prevents* you from either interacting
371
- with the UI or directly manipulating your application via calls into the model
372
- from the `GivenDriver`, both should be avoided. In the first case, the
373
- `GivenDriver`'s purpose is to describe state that exists *before* the user
374
- interaction that is being tested. Although this state may be the result of a
375
- previous user interaction, your tests will be much, much faster if you create
376
- this state via API calls rather than driving a web browser.
377
-
378
- In the second case, by avoiding the manipulation of your applications's state at the
379
- code level and instead doing so via an external API, it is much less likely that
380
- you will create a state that your application can't actually get into in a
381
- production environment. Additionally, this opens up the possibility of running
382
- your tests against a "remote" server where you would not have access to the
383
- application internals. ("Remote" in the sense that it is not in the same Ruby
384
- process as your running tests, although it may or may not be on the same
385
- machine. Note that this is not currently possible with Kookaburra due to our
386
- reliance on Rack::Test.)
357
+ Regardless of the type of APIDriver, the content of your application's APIDriver
358
+ should consist mainly of mappings between discrete actions and HTTP requests to
359
+ the specified URL paths. Each driver will implement `#post`, `#get`, `#put` and
360
+ `#delete` in such a way that any Ruby data structure provided as parameters will
361
+ be appropriately translated to the API's required data format, and any response
362
+ body from the API request will be translated into a Ruby data structure and
363
+ returned.
387
364
 
388
365
  #### UI Driver ####
389
366
 
390
367
  `Kookaburra::UIDriver` provides the necessary tools for driving your
391
- application's user interface using the Window Driver pattern. You will subclass
368
+ application's user interface with the Window Driver pattern. You will subclass
392
369
  `Kookaburra::UIDriver` for your application and implement your testing DSL
393
370
  within your subclass:
394
371
 
@@ -464,9 +441,10 @@ You describe the various user interface components by sub-classing
464
441
  `Kookaburra::APIDriver`, `Kookaburra::UIDriver` and
465
442
  `Kookaburra::UIDriver::UIComponent` rely on the Application Driver layer to
466
443
  interact with your application. In the case of the `APIDriver`, Kookaburra uses
467
- `Kookaburra::RackDriver` to send HTTP requests to your application. The `UIDriver` and
468
- `UIComponent` rely on whatever is passed to `Kookaburra.new` as the `:browser`
469
- option. Presently, we have only used Capybara as the application driver for Kookaburra.
444
+ the [Patron] [Patron] library to send HTTP requests to your application. The
445
+ `UIDriver` and `UIComponent` rely on whatever is passed to `Kookaburra.new` as
446
+ the `:browser` option. Presently, we have only used Capybara as the application
447
+ driver for Kookaburra.
470
448
 
471
449
  It's possible that something other than Capybara could be passed in, as long as
472
450
  that something presented the same API. In reality, using something other than
@@ -502,3 +480,4 @@ further details.
502
480
  [RSpec]: http://rspec.info "RSpec.info: home"
503
481
  [Cucumber]: http://cukes.info/ "Cucumber - Making BDD fun"
504
482
  [Pull Request]: https://github.com/projectdx/kookaburra/pull/new/master "Send a pull request - GitHub"
483
+ [Patron]: https://github.com/toland/patron "toland/patron"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.18.3
1
+ 0.20.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.18.3"
8
+ s.version = "0.20.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-03-18"
12
+ s.date = "2012-03-22"
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 = [
@@ -34,18 +34,16 @@ Gem::Specification.new do |s|
34
34
  "lib/kookaburra/exceptions.rb",
35
35
  "lib/kookaburra/given_driver.rb",
36
36
  "lib/kookaburra/json_api_driver.rb",
37
+ "lib/kookaburra/mental_model.rb",
37
38
  "lib/kookaburra/null_browser.rb",
38
- "lib/kookaburra/rack_driver.rb",
39
- "lib/kookaburra/test_data.rb",
40
39
  "lib/kookaburra/test_helpers.rb",
41
40
  "lib/kookaburra/ui_driver.rb",
42
41
  "lib/kookaburra/ui_driver/ui_component.rb",
43
- "lib/kookaburra/utils/active_record_shared_connection.rb",
44
42
  "spec/integration/test_a_rack_application_spec.rb",
43
+ "spec/kookaburra/api_driver_spec.rb",
45
44
  "spec/kookaburra/json_api_driver_spec.rb",
45
+ "spec/kookaburra/mental_model_spec.rb",
46
46
  "spec/kookaburra/null_browser_spec.rb",
47
- "spec/kookaburra/rack_driver_spec.rb",
48
- "spec/kookaburra/test_data_spec.rb",
49
47
  "spec/kookaburra/test_helpers_spec.rb",
50
48
  "spec/kookaburra/ui_driver/ui_component_spec.rb",
51
49
  "spec/kookaburra/ui_driver_spec.rb",
@@ -65,7 +63,7 @@ Gem::Specification.new do |s|
65
63
  s.add_runtime_dependency(%q<basic_object>, [">= 0"])
66
64
  s.add_runtime_dependency(%q<i18n>, [">= 0"])
67
65
  s.add_runtime_dependency(%q<activesupport>, [">= 3.0"])
68
- s.add_runtime_dependency(%q<rack-test>, [">= 0"])
66
+ s.add_runtime_dependency(%q<patron>, [">= 0"])
69
67
  s.add_development_dependency(%q<rspec>, [">= 0"])
70
68
  s.add_development_dependency(%q<capybara>, [">= 0"])
71
69
  s.add_development_dependency(%q<yard>, [">= 0"])
@@ -78,7 +76,7 @@ Gem::Specification.new do |s|
78
76
  s.add_dependency(%q<basic_object>, [">= 0"])
79
77
  s.add_dependency(%q<i18n>, [">= 0"])
80
78
  s.add_dependency(%q<activesupport>, [">= 3.0"])
81
- s.add_dependency(%q<rack-test>, [">= 0"])
79
+ s.add_dependency(%q<patron>, [">= 0"])
82
80
  s.add_dependency(%q<rspec>, [">= 0"])
83
81
  s.add_dependency(%q<capybara>, [">= 0"])
84
82
  s.add_dependency(%q<yard>, [">= 0"])
@@ -92,7 +90,7 @@ Gem::Specification.new do |s|
92
90
  s.add_dependency(%q<basic_object>, [">= 0"])
93
91
  s.add_dependency(%q<i18n>, [">= 0"])
94
92
  s.add_dependency(%q<activesupport>, [">= 3.0"])
95
- s.add_dependency(%q<rack-test>, [">= 0"])
93
+ s.add_dependency(%q<patron>, [">= 0"])
96
94
  s.add_dependency(%q<rspec>, [">= 0"])
97
95
  s.add_dependency(%q<capybara>, [">= 0"])
98
96
  s.add_dependency(%q<yard>, [">= 0"])