README.md in rspec-rails-api-0.1.5 vs README.md in rspec-rails-api-0.2.0
- old
+ new
@@ -1,6 +1,6 @@
-# RSpecRailsApiDoc
+# RSpec-rails-api
> An RSpec plugin to test Rails api responses and generate swagger
> documentation
**This is a work in progress** but you're welcome to help, test, submit
@@ -11,11 +11,11 @@
As the gem is not yet published, you have to specify its git repository
in order to test it.
Add this line to your application's Gemfile:
-```rb
+```ruby
gem 'rspec-rails-api'
```
And then execute:
@@ -27,26 +27,28 @@
Configuration should be made manually for now:
**spec/acceptance_helper.rb**
-```rb
+```ruby
require 'rails_helper'
require 'rspec_rails_api'
RSpec.configure do |config|
config.include RSpec::Rails::Api::DSL::Example
end
renderer = RSpec::Rails::Api::OpenApiRenderer.new
-renderer.api_servers = [{ url: 'https://example.com' }]
-renderer.api_title = 'A nice API for a nice application'
+# Options here should be customized
+renderer.api_title = 'YourProject API'
renderer.api_version = '1'
-renderer.api_description = 'Access update data in this project'
-# renderer.api_tos = 'http://example.com/tos.html'
-# renderer.api_contact = { name: 'Admin', email: 'admin@example.com', 'http://example.com/contact' }
-# renderer.api_license = { name: 'Apache', url: 'https://opensource.org/licenses/Apache-2.0' }
+renderer.api_description = 'Manage data on YourProject'
+# Options below are optional
+renderer.api_servers = [{ url: 'https://example.com' }]
+renderer.api_tos = 'http://example.com/tos.html'
+renderer.api_contact = { name: 'Admin', email: 'admin@example.com', url: 'http://example.com/contact' }
+renderer.api_license = { name: 'Apache', url: 'https://opensource.org/licenses/Apache-2.0' }
RSpec.configuration.after(:context, type: :acceptance) do |context|
renderer.merge_context context.class.metadata[:rrad].to_h
end
@@ -56,18 +58,18 @@
end
```
**spec/rails_helper.rb**
-```rb
+```ruby
# ...
RSpec::Rails::DIRECTORY_MAPPINGS[:acceptance] = %w[spec acceptance]
RSpec.configure do |config|
# ...
- config.include RSpec::Rails::RequestExampleGroup, :type => :acceptance
+ config.include RSpec::Rails::RequestExampleGroup, type: :acceptance
end
```
## Configuration
@@ -75,11 +77,11 @@
### Integration with Devise
To use `sign_in` and `sign_out` from Devise in the acceptance tests, create a Devise support file:
-```rb
+```ruby
# spec/support/devise.rb
module DeviseAcceptanceSpecHelpers
include Warden::Test::Helpers
def sign_in(resource_or_scope, resource = nil)
@@ -95,38 +97,38 @@
end
```
Load this file in `rails_helper.rb`:
-```rb
+```ruby
#...
# Add additional requires below this line. Rails is not loaded until this point!
require 'support/devise'
#...
```
Include the helper for acceptance specs:
-```rb
+```ruby
RSpec.configure do |config|
config.include DeviseAcceptanceSpecHelpers, type: :acceptance
end
```
You can now use the methods as usual:
-```rb
+```ruby
# In a before block
before do
sign_in #...
end
# In examples
#...
- for_code 200, 'Success' do |example|
+ for_code 200, 'Success' do |url|
sing_in #...
- visit example
+ visit url
#...
end
#...
```
@@ -140,28 +142,24 @@
If you want to generate the documentation without testing the endpoints
(and thus, without examples in generated files), use the `DOC_ONLY`
environment variable:
-```rb
+```sh
DOC_ONLY=true bundle exec rails spec
```
-For now, files are saved as `tmp/out.json` and `tmp/out.yml`.
-
-There is nothing to customize the file headers (info, license, ...) yet.
-
## Writing specs
-There is a [commented example](examples/commented.rb) available in
-`doc/`.
+There is a [commented example](dummy/spec/acceptance/posts_spec.rb) available in
+`dummy/spec/acceptance`.
The idea is to have a simple DSL, and declare things like:
**spec/acceptance/users_spec.rb**
-```rb
+```ruby
require 'acceptance_helper'
RSpec.describe 'Users', type: :acceptance do
resource 'Users', 'Manage users'
@@ -172,12 +170,12 @@
created_at: { type: :datetime, description: 'Creation date' },
updated_at: { type: :datetime, description: 'Modification date' },
url: { type: :string, description: 'URL to this category' }
on_get '/api/users/', 'Users list' do
- for_code 200, 'Success response' do |example|
- visit example
+ for_code 200, 'Success response' do |url|
+ visit url
expect(response).to have_many defined :user
end
end
on_put '/api/users/:id', 'Users list' do
@@ -189,41 +187,40 @@
email: { type: :string, required: false, description: 'New email' },
role: { type: :string, required: false, description: 'New role' },
}
}
- for_code 200, 'Success response' do |example|
- visit example
+ for_code 200, 'Success response' do |url|
+ visit url
expect(response).to have_one defined :user
end
end
end
```
### DSL
#### Example groups
-##### `resource(name, description)`
+##### `resource(type, description)`
Starts a resource description.
- It must be called before any other documentation calls.
- It should be in the first `describe block`
-##### `entity(name, fields)`
+##### `entity(type, fields)`
-Describes an entity for the documentation. The name is not visible, so
-you can put whatever fits (i.e: `:account`, `:user` if the content
-differs)
+Describes an entity for the documentation. The type is only a reference,
+you can put whatever fits (i.e: `:account`, `:user`, ...).
-They are ideally in the main `describe` block.
+They should be in the main `describe` block.
-- `name` is a symbol
+- `type` is a symbol
- `description` is a hash of attributes
-```rb
+```ruby
{
id: { type: :integer, desc: 'The resource identifier' },
name: { type: :string, desc: 'The resource name' },
# ...
}
@@ -251,11 +248,11 @@
###### Objects and arrays
To describe complex structures, use `:object` with `:attributes` and
`:array` `:of` something:
-```rb
+```ruby
entity :friend,
name: { type: :string, required: false, description: 'Friend name' }
entity :user,
id: { type: :number, required: false, description: 'Identifier' },
@@ -275,10 +272,19 @@
inline.
Both `:of` and `attributes` may be a hash of fields or a symbol. If they
are omitted, they will be documented, but responses won't be validated.
+##### `parameters(type, fields)`
+Describe path or request parameters. The type is only a reference,
+use whatever makes sense. These parameters will be present in
+documentation, only if they are referenced by a `request_params` or
+`path_params` call.
+
+Fields have the structure of the hash you would give to `request_params`
+or `path_params` (see each method later in this documentation).
+
##### `on_<xxx>(url, description, &block)`
Defines an URL.
- `url` should be a relative URL to an existing endpoint (i.e.:
@@ -292,41 +298,57 @@
- `on_post`
- `on_put`
- `on_patch`
- `on_delete`
-##### `path_params(<hash_of_attributes>)`
+##### `path_params(fields: nil, defined: nil)`
Defines the path parameters that are used in the URL.
-```rb
+```ruby
on_get '/api/users/:id/posts/:post_slug?full=:full_post' do
- path_params id: type: :integer, description: 'The user ID',
- post_slug: type: :string, description: 'The post slug',
- full_post: type: :boolean, required: false, description: 'Returns the full post if `true`, or only an excerpt'
+ path_params fields: {
+ id: { type: :integer, description: 'The user ID' },
+ post_slug: { type: :string, description: 'The post slug' },
+ full_post: { type: :boolean, required: false, description: 'Returns the full post if `true`, or only an excerpt' } }
# ...
end
```
- `type` is the field type (check _entity definition_ for a list).
- `description` should be some valid
[CommonMark](https://commonmark.org/)
- `required` is optional an defaults to `true`.
-##### `request_params(<hash_of_attributes>)`
+Alternative with defined parameters:
+```ruby
+parameters :users_post_path_params,
+ id: { type: :integer, description: 'The user ID' },
+ post_slug: { type: :string, description: 'The post slug' }
+
+on_get '/api/users/:id/posts/:post_slug' do
+ path_params defined: :users_post_path_params
+
+ #...
+end
+```
+
+##### `request_params(attributes: nil, defined: nil)`
+
Defines the format of the JSON payload. Type `object` is supported, so
nested elements can be described:
-```rb
+```ruby
on_post '/api/items' do
- request_params item: { type: :object, required: true, properties: {
- name: { type: integer, description: 'The name of the new item', required: true },
- notes: { type: string, description: 'Additional notes' }
- },
- }
+ request_params attributes: {
+ item: { type: :object, required: true, properties: {
+ name: { type: integer, description: 'The name of the new item', required: true },
+ notes: { type: string, description: 'Additional notes' }
+ } }
+ }
#...
end
```
An attribute should have the following form:
@@ -340,10 +362,27 @@
`type` can be `:object` if the attribute contains other attributes.
- `required` is optional an defaults to `false`.
- `properties` is a hash of params and is only used if `type: :object`
- `of` is a hash of params and is only used if `type: :array`
+Alternative with defined parameters:
+
+```ruby
+parameters :item_form_params,
+ item: { type: :object, required: true, properties: {
+ name: { type: integer, description: 'The name of the new item', required: true },
+ notes: { type: string, description: 'Additional notes' }
+ }
+ }
+
+on_post '/api/items' do
+ request_params defined: :item_form_params
+
+ #...
+end
+```
+
##### `for_code(http_status, description, doc_only: false &block)`
Describes the desired output for a precedently defined URL.
Block takes one required argument, that should be passed to `visit`.
@@ -365,14 +404,14 @@
to test.
Once again, you have to pass an argument to the block if you use
`visit`.
-```rb
+```ruby
# ...
- for_code 200 'A successful response' do |example|
- visit example
+ for_code 200, 'A successful response' do |url|
+ visit url
# ...
end
# ...
```
@@ -393,13 +432,13 @@
value is needed)
- `payload`: a hash of values to send. Ignored for GET and DELETE
requests
- `headers`: a hash of custom headers.
-```rb
-for_code 200, 'Success' do |example|
- visit example
+```ruby
+for_code 200, 'Success' do |url|
+ visit url
end
```
#### Matchers
@@ -408,13 +447,13 @@
Expects the compared content to be a hash with the same keys as a
defined entity.
It should be compared against a hash or a `response` object:
-```rb
+```ruby
#...
-entity user:
+entity :user,
id: { type: :integer, desc: 'The id' },
name: { type: :string, desc: 'The name' }
#...
@@ -431,15 +470,15 @@
Expects the compared content to be an array of hashes with the same keys
as a defined entity.
It should be compared against an array or a `response` object:
-```rb
+```ruby
#...
-entity user:
+entity :user,
id: { type: :integer, desc: 'The id' },
- name: { type: :string, desc: 'The name }'
+ name: { type: :string, desc: 'The name' }
#...
expect([{id: 2, name: 'Jessica'}, {name: 'John'}]).to have_many defined :user # Fails because `id` is missing in the second entry
@@ -454,11 +493,11 @@
### Contexts
Contexts will break the thing. This is due to how the gem builds its
metadata, relying on the parents metadata. You have to stick to the DSL.
-```rb
+```ruby
RSpec.describe 'Categories', type: :request do
describe 'Categories'
context 'Authenticated' do
on_get '/api/categories', 'List all categories' do
@@ -492,17 +531,41 @@
There is no support for file fields yet.
## Development
After checking out the repo, run `bin/setup` to install dependencies.
-Then, run `rake spec` to run the tests. You can also run `bin/console`
+Then, run `bundle exec rspec` to run the tests. You can also run `bin/console`
for an interactive prompt that will allow you to experiment.
-To install this gem onto your local machine, run `bundle exec rake
-install`. To release a new version, update the version number in
-`version.rb`, and then run `bundle exec rake release`, which will create
-a git tag for the version, push git commits and tags, and push the
-`.gem` file to [rubygems.org](https://rubygems.org).
+### Testing
+
+#### Dummy application
+
+A small Rails application is available as an example, in the `dummy` directory.
+If you write new features in the library, you'll have to update the examples to
+make them pass their tests:
+
+```shell
+cd dummy
+bundle exec rspec
+```
+
+Doing so will also update the fixtures used by some of the library tests.
+
+Please don't commit the fixtures unless the changes in them are directly related
+to the changes in the library.
+
+#### Code linting
+
+We use Rubocop here, with a releset for the library, and another for the dummy
+application:
+
+```shell
+bundle exec rubocop
+
+cd dummy
+bundle exec rubocop
+```
## Contributing
Bug reports and pull requests are welcome on GitLab at
https://gitlab.com/experimentslabs/rspec-rails-api/issues. This